วิธีกรองอาร์เรย์ของออบเจ็กต์สำหรับการจับคู่ที่ไม่คำนึงถึงขนาดตัวพิมพ์จากคีย์ออบเจ็กต์ใดๆ

ฉันมีโค้ดตัวอย่างนี้อยู่ที่นี่ และฉันกำลังพยายามกรองออบเจ็กต์ที่ตรงกันโดยไม่ทำให้ความซับซ้อนหรือประสิทธิภาพของโค้ดระเบิด:

รหัสนี้กรองการจับคู่ตามคีย์ที่กำหนดไว้อย่างชัดเจนและไม่คำนึงถึงขนาดตัวพิมพ์

const people = [
  { firstName: 'Bob', lastName: 'Smith', status: 'single' },
  { firstName: 'bobby', lastName: 'Suxatcapitalizing', status: 'single' },
  { firstName: 'Jim', lastName: 'Johnson', status: 'complicated' },
  { firstName: 'Sally', lastName: 'Fields', status: 'relationship' },
  { firstName: 'Robert', lastName: 'Bobler', status: 'single' },
  { firstName: 'Johnny', lastName: 'Johannson', status: 'complicated' },
  { firstName: 'Whaley', lastName: 'McWhalerson', status: 'relationship'
    rogueBonusKey: 'bob likes salmon' },
]

const searchString = 'Bob'

const found = people.filter((person) => {
  if (person.firstName === searchString) return true
})

console.log(found)

เป้าหมาย:

  1. ฉันต้องการให้ตรงกับตัวพิมพ์เล็กและตัวพิมพ์ใหญ่
  2. ฉันต้องการให้ส่งคืนการแข่งขันจากคีย์ใดก็ได้
  3. ฉันต้องการให้พบว่าการใช้ contains ไม่ตรงกันทุกประการ

บางสิ่งเช่นนี้:

// const people = [
//   { firstName: 'Bob', lastName: 'Smith', status: 'single' },
//   { firstName: 'bobby', lastName: 'Suxatcapitalizing', status: 'single' },
//   { firstName: 'Jim', lastName: 'Johnson', status: 'complicated' },
//   { firstName: 'Sally', lastName: 'Fields', status: 'relationship' },
//   { firstName: 'Robert', lastName: 'Bobler', status: 'single' },
//   { firstName: 'Johnny', lastName: 'Johannson', status: 'complicated' },
//   { firstName: 'Whaley', lastName: 'McWhalerson', status: 'relationship'
//     rogueBonusKey: 'bob likes salmon' },
// ]

// const searchString = 'bob'

// ... magic

// console.log(found)

// { firstName: 'Bob', lastName: 'Smith', status: 'single' },
// { firstName: 'bobby', lastName: 'Suxatcapitalizing', status: 'single' },
// { firstName: 'Robert', lastName: 'Bobler', status: 'single' },
// { firstName: 'Whaley', lastName: 'McWhalerson', status: 'relationship'
//   rogueBonusKey: 'bob likes salmon' },

ฉันได้ค้นหาเอกสารที่เกี่ยวข้องกับ Array.filter() แล้ว และฉันก็สามารถสร้างวิธีแก้ปัญหาที่เกี่ยวข้องกับ Array.reduce() และการวนซ้ำสิ่งต่าง ๆ ด้วย Object.keys(obj).forEach() ได้อย่างแน่นอน แต่ฉันต้องการทราบว่ามีวิธีที่กระชับและมีประสิทธิภาพในการจัดการกับการค้นหาที่ไม่ชัดเจนประเภทนี้หรือไม่

บางสิ่งเช่นนี้:

const people = [
  { firstName: 'Bob', lastName: 'Smith', status: 'single' },
  { firstName: 'bobby', lastName: 'Suxatcapitalizing', status: 'single' },
  { firstName: 'Jim', lastName: 'Johnson', status: 'complicated' },
  { firstName: 'Sally', lastName: 'Fields', status: 'relationship' },
  { firstName: 'Robert', lastName: 'Bobler', status: 'single' },
  { firstName: 'Johnny', lastName: 'Johannson', status: 'complicated' },
  { firstName: 'Whaley', lastName: 'McWhalerson', status: 'relationship' },
    rogueBonusKey: 'bob likes salmon' },
]

const searchString = 'Bob'

const found = people.filter((person) => {
  if (person.toString().indexOf(searchString).toLowerCase !== -1) return true
})

console.log(found)

[แก้ไข] ใช้งานได้แน่นอน แต่จะยอมรับได้หรือไม่

const people = [
  { firstName: 'Bob', lastName: 'Smith', status: 'single' },
  { firstName: 'bobby', lastName: 'Suxatcapitalizing', status: 'single' },
  { firstName: 'Jim', lastName: 'Johnson', status: 'complicated' },
  { firstName: 'Sally', lastName: 'Fields', status: 'relationship' },
  { firstName: 'Robert', lastName: 'Bobler', status: 'single' },
  { firstName: 'Johnny', lastName: 'Johannson', status: 'complicated' },
  { firstName: 'Whaley', lastName: 'McWhalerson', status: 'relationship',
    rogueBonusKey: 'bob likes salmon' },
]

const searchString = 'Bob'

const found = people.filter((person) => {
  const savageMatch = JSON.stringify(person)
    .toLowerCase()
    .indexOf(searchString.toLowerCase()) !== -1

  console.log(savageMatch)
  if (savageMatch) return true
})

console.log(found)

เพิ่มประสิทธิภาพพื้นที่หน่วยความจำ:

const found = people.filter((person) => JSON.stringify(person)
    .toLowerCase()
    .indexOf(searchString.toLowerCase()) !== -1
)

แปลงเป็นฟังก์ชัน:

const fuzzyMatch = (collection, searchTerm) =>
  collection.filter((obj) => JSON.stringify(obj)
    .toLowerCase()
    .indexOf(searchTerm.toLowerCase()) !== -1
)

console.log(fuzzyMatch(people, 'bob'))

มีคำตอบที่ดีอยู่ที่นี่ จนถึงตอนนี้ ฉันได้เลือกสิ่งนี้สำหรับความต้องการในการกรองของฉัน:

const people = [
  { imageURL: 'http://www.alice.com/goat.jpeg', firstName: 'Bob', lastName: 'Smith', status: 'single' },
  { firstName: 'bobby', lastName: 'Suxatcapitalizing', status: 'single' },
  { firstName: 'Jim', lastName: 'Johnson', status: 'complicated' },
  { firstName: 'Sally', lastName: 'Fields', status: 'relationship' },
  { firstName: 'Robert', lastName: 'Bobler', status: 'single' },
  { firstName: 'Johnny', lastName: 'Johannson', status: 'complicated' },
  {
    firstName: 'Ronald', lastName: 'McDonlad', status: 'relationship',
    rogueBonusKey: 'bob likes salmon'
  },
  {
    imageURL: 'http://www.bob.com/cats.jpeg', firstName: 'Whaley', lastName: 'McWhalerson', status: 'relationship',
    rogueBonusKey: 'bob hates salmon'
  },
]

const searchString = 'bob'

const options = {
  caseSensitive: false,
  excludedKeys: ['imageURL', 'firstName'],
}

const customFind = (collection, term, opts) => {
  const filterBy = () => {
    const searchTerms = (!opts.caseSensitive) ? new RegExp(term, 'i') : new RegExp(term)
    return (obj) => {
      for (const key of Object.keys(obj)) {
        if (searchTerms.test(obj[key]) &&
          !opts.excludedKeys.includes(key)) return true
      }
      return false
    }
  }
  return collection.filter(filterBy(term))
}

const found = customFind(people, searchString, options)

console.log(found)

ฉันทำให้มันสามารถรองรับการพิจารณาตัวพิมพ์เล็กและตัวพิมพ์ใหญ่และยกเว้นคีย์เฉพาะได้


person agm1984    schedule 05.11.2017    source แหล่งที่มา
comment
ผลลัพธ์ที่คาดหวังคือออบเจ็กต์ที่ตรงกันทั้งหมดหรือเฉพาะคุณสมบัติคู่ค่าเท่านั้น   -  person guest271314    schedule 05.11.2017
comment
จับคู่วัตถุทั้งหมด   -  person agm1984    schedule 05.11.2017


คำตอบ (6)


หากเราถือว่าคุณสมบัติทั้งหมดเป็นสตริง คุณอาจดำเนินการดังต่อไปนี้

const people = [
  // ...
]

const searchString = 'Bob'

const filterBy = (term) => {
  const termLowerCase = term.toLowerCase()
  return (person) =>
    Object.keys(person)
      .some(prop => person[prop].toLowerCase().indexOf(termLowerCase) !== -1)
}

const found = people.filter(filterBy(searchString))

console.log(found)

อัปเดต: โซลูชันทางเลือกที่มี RegExp และรุ่นเก่ากว่า :) แต่เร็วกว่า 2 เท่า

const people = [
  // ...
]

const searchString = 'Bob'

const escapeRegExp = (str) => // or better use 'escape-string-regexp' package
  str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")


const filterBy = (term) => {
  const re = new RegExp(escapeRegExp(term), 'i')
  return person => {
    for (let prop in person) {
      if (!person.hasOwnProperty(prop)) {
        continue;
      }
      if (re.test(person[prop])) {
        return true;
      }
    }
    return false;        
  }
}

const found = people.filter(filterBy(searchString))
person Dmitry Druganov    schedule 05.11.2017
comment
นี่จะดีกว่าการทำให้เป็นเส้นตรงอย่างแน่นอน - person agm1984; 05.11.2017
comment
ฉันเพิ่งรู้ว่าควรเพิ่มคีย์ที่แยกออกด้วย ดังนั้นจึงสามารถใช้เป็นวัตถุประสงค์ทั่วไปได้ ดูเหมือนว่าคุณควรจะสนับสนุนอย่างรวดเร็ว - person agm1984; 05.11.2017
comment
ฉันชอบการใช้ .some() ของคุณ ฉันไม่รู้ว่ามีอยู่จริง ฉันจะทำเครื่องหมายคำตอบนี้ว่าถูกต้องหลังจาก Array.some(more time has passed) โดยสมมติว่าไม่มีใครใช้อัญมณีข้อมูลที่บ้าคลั่ง - person agm1984; 05.11.2017
comment
อัปเดตการทดสอบของคุณด้วยวิธีแก้ปัญหาล่าสุด: jsperf.com/fuzzy-match-objects-2 - person Dmitry Druganov; 05.11.2017
comment
หากคุณลบ Escape ออก มันควรจะเร็วกว่าโซลูชัน @ guest271314 เล็กน้อย จริงๆ แล้ววิธีแก้ปัญหาของเขาก็น่าจะมีการหลบหนีเช่นกัน - person Dmitry Druganov; 05.11.2017
comment
ในกรณีของคุณ การตรวจสอบคุณสมบัติของตัวเองก็ไม่จำเป็นเช่นกัน ดังนั้นการเพิ่มประสิทธิภาพระดับย่อยนี้ควรเพิ่มประสิทธิภาพเล็กน้อยด้วย :) - person Dmitry Druganov; 05.11.2017
comment
คำตอบที่อัปเดตของคุณค่อนข้างสวยงาม ฉันพบปัญหาในการพยายามเพิ่ม excludedKeys ในคำตอบของ guest271314 ฉันจะไม่เรียกคุณว่าโรงเรียนเก่ากว่านี้ for in และ for of ลูปรองรับ async/await ของคุณดูเหมือนว่าจะเป็นเรื่องง่ายที่จะเพิ่มการตั้งค่าเพิ่มเติมเพิ่มเติม ฉันคิดว่าฉันสามารถดำเนินการให้เสร็จสิ้นได้ในเร็วๆ นี้ด้วยการอัปเดตของคุณ ฉันจะโพสต์คำตอบสุดท้ายเมื่อฉันทำเสร็จแล้ว คำตอบของแขกนั้นดีมากแต่ก็เข้มงวดมาก มันเป็นบทกวีมากแม้ว่า คุณจัดหาตะขอเพิ่มเติมด้วย for in - person agm1984; 05.11.2017
comment
ฉันแทนที่คุณด้วย in ด้วย for (const key of Object.keys(obj)) ;) - person agm1984; 05.11.2017
comment
@DmitryDruganov มีวิธีใดบ้างที่จะทำเช่นเดียวกันหากอาร์เรย์ของวัตถุของฉันมีรวมถึงตัวเลขด้วยหรือไม่ - person legacy; 25.07.2021

คุณควรลอง fusejs http://fusejs.io/ มีการตั้งค่าที่น่าสนใจบางอย่าง เช่น ขีดจำกัด ซึ่งทำให้เกิดข้อผิดพลาดในการพิมพ์ผิด (0.0 = สมบูรณ์แบบ 1.0 = จับคู่อะไรก็ได้) และ keys เพื่อระบุคีย์ใด ๆ ที่คุณต้องการค้นหา

const people = [
  { firstName: 'Bob', lastName: 'Smith', status: 'single' },
  { firstName: 'bobby', lastName: 'Suxatcapitalizing', status: 'single' },
  { firstName: 'Jim', lastName: 'Johnson', status: 'complicated' },
  { firstName: 'Sally', lastName: 'Fields', status: 'relationship' },
  { firstName: 'Robert', lastName: 'Bobler', status: 'single' },
  { firstName: 'Johnny', lastName: 'Johannson', status: 'complicated' },
  { firstName: 'Whaley', lastName: 'McWhalerson', status: 'relationship'
    rogueBonusKey: 'bob likes salmon' },
]

const fuseOptions = {
  caseSensitive: false,
  shouldSort: true,
  threshold: 0.2,
  location: 0,
  distance: 100,
  maxPatternLength: 32,
  minMatchCharLength: 1,
  keys: [
    "firstName",
    "lastName",
    "rogueBonusKey",
  ]
};


const search = (txt) => {
  const fuse = new Fuse(people, fuseOptions);
  const result = fuse.search(txt);
  return result;
}
person Doppio    schedule 06.11.2017

คุณต้องกรองอาร์เรย์ จากนั้นกรองแต่ละคีย์ในออบเจ็กต์เพื่อให้ตรงกับนิพจน์ทั่วไป ตัวอย่างนี้แบ่งปัญหาออกเป็นหน้าที่รับผิดชอบเดี่ยวๆ และเชื่อมโยงปัญหาเหล่านั้นกับแนวคิดเชิงฟังก์ชัน เช่น

รวมการทดสอบประสิทธิภาพด้วย ใน Chrome ซึ่งเร็วกว่าตัวอย่างของ Dmitry อย่างสม่ำเสมอ ฉันยังไม่ได้ทดสอบเบราว์เซอร์อื่นใด นี่อาจเป็นเพราะการปรับให้เหมาะสมที่ Chrome ใช้เพื่ออนุญาตให้ jit ประมวลผลสคริปต์ได้เร็วขึ้น เมื่อโค้ดถูกแสดงเป็นฟังก์ชันความรับผิดชอบเดี่ยวเล็กๆ ที่รับข้อมูลเพียงประเภทเดียวเป็นอินพุต และข้อมูลประเภทหนึ่งเป็นเอาต์พุต

เนื่องจากการทดสอบจะใช้เวลาประมาณ 4 วินาทีในการโหลด

const people = [
  { firstName: 'Bob', lastName: 'Smith', status: 'single' },
  { firstName: 'bobby', lastName: 'Suxatcapitalizing', status: 'single' },
  { firstName: 'Jim', lastName: 'Johnson', status: 'complicated' },
  { firstName: 'Sally', lastName: 'Fields', status: 'relationship' },
  { firstName: 'Robert', lastName: 'Bobler', status: 'single' },
  { firstName: 'Johnny', lastName: 'Johannson', status: 'complicated' },
  { firstName: 'Whaley', lastName: 'McWhalerson', status: 'relationship', rogueBonusKey: 'bob likes salmon' },
]

// run a predicate function over each key of an object
// const hasValue = f => o => 
//  Object.keys(o).some(x => f(o[x]))
const hasValue = f => o => {
  let key
  for (key in o) {
    if (f(o[key])) return true
  }
  return false
}

// convert string to regular expression
const toReg = str => 
  new RegExp(str.replace(/\//g, '//'), 'gi')

// test a string with a regular expression
const match = reg => x => 
  reg.test(x)

// filter an array by a predicate
// const filter = f => a => a.filter(a)
const filter = f => a => {
  const ret = []
  let ii = 0
  let ll = a.length
  for (;ii < ll; ii++) {
    if (f(a[ii])) ret.push(a[ii])
  }
  return ret
}

// **magic**
const filterArrByValue = value => {
  // create a regular expression based on your search value
  // cache it for all filter iterations
  const reg = toReg(value)
  // filter your array of people
  return filter(
    // only return the results that match the regex
    hasValue(match(reg))
  )
}

// create a function to filter by the value 'bob'
const filterBob = filterArrByValue('Bob')

// ########################## UNIT TESTS ########################## //

console.assert('hasValue finds a matching value', !!hasValue(x => x === 'one')({ one: 'one' }))
console.assert('toReg is a regular expression', toReg('reg') instanceof RegExp)
console.assert('match finds a regular expression in a string', !!match(/test/)('this is a test'))
console.assert('filter filters an array', filter(x => x === true)([true, false]).length === 1)

// ##########################   RESULTS   ########################## //

console.log(
  // run your function passing in your people array
  'find bob',
  filterBob(people)
)

console.log(
  // or you could call both of them at the same time
  'find salmon',
  filterArrByValue('salmon')(people)
)

// ########################## BENCHMARKS ########################## //

// dmitry's first function
const filterBy = (term) => {
  const termLowerCase = term.toLowerCase()
  return (person) =>
    Object.keys(person)
      .some(prop => person[prop].toLowerCase().indexOf(termLowerCase) !== -1)
}

// dmitry's updated function
const escapeRegExp = (str) => // or better use 'escape-string-regexp' package
  str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")


const filterBy2 = (term) => {
  const re = new RegExp(escapeRegExp(term), 'i')
  return person => {
    for (let prop in person) {
      if (!person.hasOwnProperty(prop)) {
        continue;
      }
      if (re.test(person[prop])) {
        return true;
      }
    }
    return false;        
  }
}

// test stringify - incredibly slow
const fuzzyMatch = (collection, searchTerm) =>
  collection.filter((obj) => JSON.stringify(obj)
    .toLowerCase()
    .indexOf(searchTerm.toLowerCase()) !== -1
)

new Benchmark({ iterations: 1000000 })
  // test my function - fastest
  .add('synthet1c', function() {
    filterBob(people)
  })
  .add('dmitry', function() {
    people.filter(filterBy('Bob'))
  })
  .add('dmitry2', function() {
    people.filter(filterBy2('Bob'))
  })
  .add('guest', function() {
    fuzzyMatch(people, 'Bob')
  })
  .run()
<link rel="stylesheet" type="text/css" href="https://codepen.io/synthet1c/pen/WrQapG.css">
<script src="https://codepen.io/synthet1c/pen/WrQapG.js"></script>

person synthet1c    schedule 05.11.2017
comment
นี่เป็นสิ่งที่ดีจริงๆ ขอบคุณ. พรุ่งนี้ฉันต้องดูให้ละเอียดกว่านี้ ฉันสงสัยว่าสิ่งนี้มีประสิทธิภาพอย่างไรเมื่อเปรียบเทียบกับโซลูชันที่อัปเดตของ Guest และ Dmitry คืนนี้ฉันขี้เกียจเกินไปที่จะพยายามสร้างอาร์เรย์ขนาดยักษ์ - person agm1984; 05.11.2017
comment
เรียกใช้ข้อมูลโค้ดเพื่อดูความแตกต่างด้านประสิทธิภาพระหว่างตัวอย่างต่างๆ - person synthet1c; 05.11.2017

คุณยังสามารถใช้ นิพจน์ทั่วไป พร้อมด้วย i modifier เพื่อ ทำการจับคู่โดยคำนึงถึงตัวพิมพ์เล็กและใหญ่และวิธีการ RegExp. ต้นแบบ.ทดสอบ()

  • สะดวกมากเมื่อคุณต้องการประเมินคุณสมบัติของวัตถุหลายอย่าง เช่น:

    new RegExp(searchString, 'i').test( 
      person.email || person.firstName || person.lastName
    )
    

รหัส:

const people = [{ firstName: 'Bob', lastName: 'Smith', status: 'single' }, { firstName: 'bobby', lastName: 'Suxatcapitalizing', status: 'single' }, { firstName: 'Jim', lastName: 'Johnson', status: 'complicated' }, { firstName: 'Sally', lastName: 'Fields', status: 'relationship' }, { firstName: 'Robert', lastName: 'Bobler', status: 'single' }, { firstName: 'Johnny', lastName: 'Johannson', status: 'complicated' }, { firstName: 'Whaley', lastName: 'McWhalerson', status: 'relationship', rogueBonusKey: 'bob likes salmon' }]

const searchString = 'Bob'

const found = people.filter(({ firstName }) => 
  new RegExp(searchString, 'i').test(firstName))

console.log(found)

person Yosvel Quintero Arguelles    schedule 10.08.2020

หากวัตถุที่ตรงกันทั้งหมดเป็นผลลัพธ์ที่คาดหวัง คุณสามารถใช้ for..of loop, Object.values(), Array.prototype.find()

consy searchString = "Bob";
const re = new RegExp(searchString, "i");
let res = [];
for (const props of Object.values(people))
  Object.values(props).find(prop => re.test(prop)) && res.push(props);
person guest271314    schedule 05.11.2017
comment
ขอบคุณ นี่มันเจ๋งเลย น่าเสียดายที่การดำเนินการด้วยเหตุผลบางอย่างใช้เวลานานกว่าโซลูชันของ Dmitry ถึง 2-3 เท่า - person agm1984; 05.11.2017
comment
@ agm1984 คุณสามารถสร้าง jsperf เพื่อสาธิตได้ไหม? - person guest271314; 05.11.2017
comment
การทดสอบที่ดีกว่าคือกับอาร์เรย์ของวัตถุนับพันรายการ ในเบราว์เซอร์ของฉัน อันแรกเร็วกว่า 2-3 เท่าโดยใช้ performance.now() - person agm1984; 05.11.2017
comment
@ agm1984 คุณลองใช้โค้ดเบราว์เซอร์ใด ที่ Chromium 61 for..of นั้นเร็วที่สุด Array.some const filterBy = (term) =› { const termLowerCase = term.toLowerCase() return (person) =› Object.keys(person) .some(prop =› person[prop ].toLowerCase().indexOf(termLowerCase) !== -1) } const found = people.filter(filterBy(searchString)) 115,822 ±3.10% ช้าลง 6% สำหรับลูป + Regex const re = new RegExp(searchString, i ) ให้ res = [] for (const อุปกรณ์ประกอบฉากของ Object.values(คน)) Object.values(props).find(prop =› re.test(prop)) && res.push(props) 122,775 ±3.10% เร็วที่สุด< /ผม> - person guest271314; 05.11.2017
comment
การทดสอบใน Chrome 61.0.3163 / Windows 10 0.0.0 - person agm1984; 05.11.2017
comment
ที่ Firefox 57 Array.some เร็วขึ้นในการรันครั้งแรก และช้าลงในการรันครั้งที่สองที่ *nix Array.some const filterBy = (term) =› { const termLowerCase = term.toLowerCase() return (person) =› Object คีย์ (บุคคล) .some(prop =› person[prop].toLowerCase().indexOf(termLowerCase) !== -1) } const found = people.filter(filterBy(searchString)) 99,771 ±4.53% ช้าลง 5% สำหรับ ของลูป + Regex const re = new RegExp(searchString, i) ให้ res = [] สำหรับ (const อุปกรณ์ประกอบฉากของ Object.values(people)) Object.values(props).find(prop =› re.test(prop)) && res.push(อุปกรณ์ประกอบฉาก) 101,249 ±0.82% เร็วที่สุด - person guest271314; 05.11.2017
comment
ฉันเห็นสิ่งเดียวกันบนเครื่องของฉัน ดูเหมือนว่าจะเบี่ยงเบนระหว่างทั้ง +/- 20% - person agm1984; 05.11.2017
comment
@ agm1984 ดู stackoverflow.com/questions/46516234/ - person guest271314; 05.11.2017
comment
ขอบคุณ โดยทั่วไปแล้ว For loops จะเร็วกว่าเสมอจากประสบการณ์ของฉัน ฉันกำลังทำงานกับรหัสของคุณอยู่ในขณะนี้ มันค่อนข้างง่ายที่จะทำให้คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่หรือไม่ - person agm1984; 05.11.2017

คุณสามารถใช้ Array.prototype.find() ได้

const people = [
  { firstName: 'Bob', lastName: 'Smith', status: 'single' },
  { firstName: 'bobby', lastName: 'Suxatcapitalizing', status: 'single' },
  { firstName: 'Jim', lastName: 'Johnson', status: 'complicated' },
  { firstName: 'Sally', lastName: 'Fields', status: 'relationship' },
  { firstName: 'Robert', lastName: 'Bobler', status: 'single' },
  { firstName: 'Johnny', lastName: 'Johannson', status: 'complicated' },
  { firstName: 'Whaley', lastName: 'McWhalerson', status: 'relationship',
    'rogueBonusKey': 'bob likes salmon' },
]

const searchString = 'Bob';

const person = people.find(({ firstName }) => firstName.toLowerCase() === searchString.toLowerCase());

console.log(person);

person xinthose    schedule 14.01.2021