Difference of two javascript arrays of objects - javascript

I have two arrays like this:
owners: [
{
userID: "58c4d7ac",
username: "John.Doe",
firstName: "John",
lastName: "Doe",
email: "John.Doe#acme.com"
},
{
userID: "68c4d7ac",
username: "User2.Name2",
firstName: "User2",
lastName: "Name2",
email: "dh#acme.com"
}
]
users: [
{
userID: "58c4d7ac",
username: "John.Doe",
firstName: "John",
lastName: "Doe",
email: "John.Doe#acme.com"
},
{
userID: "68c4d7ac",
username: "User2.Name2",
firstName: "User2",
lastName: "Name2",
email: "dh#acme.com"
},
{
userID: "88c4d7ac",
username: "User3.Name3",
firstName: "User3",
lastName: "Name3",
email: "dh#acme.com"
}
]
I would like to get an array of users which contains only the elements which are not in the owners array.
I tried different approaches. Finally, I ended up with the solution:
const usersItems = users.map(user => {
// Check whether the user is already an owner
if (owners.findIndex(owner => owner.userID === user.userID) === -1) {
return owner
} else {
return null;
}
});
console.log(usersItems);
// Filter out all items which are null
const newUsersItems = usersItems.filter(user => {
if (user) return user;
});
console.log(usersItems);
To me, it doesn't' look like a clean solution. Is there a cleaner and easier way to do this? As a result, I would like to have:
newUsers: [
{
userID: "88c4d7ac",
username: "User3.Name3",
firstName: "User3",
lastName: "Name3",
email: "dh#acme.com"
}
]

You could get rid of your map and just use the filter, (that's exactly what filter is for) somthing like
const filtered = users.filter(user => {
// Check whether the user is already an owner
return owners.findIndex(owner => owner.userID === user.userID) === -1
});
would probably work

You can use combination of filter and some functions like this:
const owners = [
{
userID: "58c4d7ac",
username: "John.Doe",
firstName: "John",
lastName: "Doe",
email: "John.Doe#acme.com"
},
{
userID: "68c4d7ac",
username: "User2.Name2",
firstName: "User2",
lastName: "Name2",
email: "dh#acme.com"
}
];
const users = [
{
userID: "58c4d7ac",
username: "John.Doe",
firstName: "John",
lastName: "Doe",
email: "John.Doe#acme.com"
},
{
userID: "68c4d7ac",
username: "User2.Name2",
firstName: "User2",
lastName: "Name2",
email: "dh#acme.com"
},
{
userID: "88c4d7ac",
username: "User3.Name3",
firstName: "User3",
lastName: "Name3",
email: "dh#acme.com"
}
];
const result = users.filter(user => !owners.some(owner => owner.userID === user.userID));
console.log(result);

First you can create a Set() with the userID's of the owners array, and then you can use Array.filter() on the users array to filter the users whose userID does not belong to the previous created set.
const owners = [
{userID: "58c4d7ac", username: "John.Doe", firstName: "John", lastName: "Doe", email: "John.Doe#acme.com"},
{userID: "68c4d7ac", username: "User2.Name2", firstName: "User2", lastName: "Name2", email: "dh#acme.com"}
];
const users = [
{userID: "58c4d7ac", username: "John.Doe", firstName: "John", lastName: "Doe", email: "John.Doe#acme.com"},
{userID: "68c4d7ac", username: "User2.Name2", firstName: "User2", lastName: "Name2", email: "dh#acme.com"},
{userID: "88c4d7ac", username: "User3.Name3", firstName: "User3", lastName: "Name3", email: "dh#acme.com"}
];
let ownerIdsSet = new Set(owners.map(x => x.userID));
let res = users.filter(x => !ownerIdsSet.has(x.userID));
console.log(res);
.as-console {background-color:black !important; color:lime;}
But why to construct a Set first?
In summary, it will improve the performance of the filtering process, particularly if the owners array is large. You should note that methods like findIndex(), find() and some() needs to traverse the array for check to the related condition while checking if the userID belongs to the Set is a O(1) calculation. However, of course, there will be an extra overload at initialization to create the mentioned Set.

You can use filter and some
const owners = [{userID:"58c4d7ac",username:"John.Doe",firstName:"John",lastName:"Doe",email:"John.Doe#acme.com"},{userID:"68c4d7ac",username:"User2.Name2",firstName:"User2",lastName:"Name2",email:"dh#acme.com"}]
const users = [{userID:"58c4d7ac",username:"John.Doe",firstName:"John",lastName:"Doe",email:"John.Doe#acme.com"},{userID:"68c4d7ac",username:"User2.Name2",firstName:"User2",lastName:"Name2",email:"dh#acme.com"},{userID:"88c4d7ac",username:"User3.Name3",firstName:"User3",lastName:"Name3",email:"dh#acme.com"}]
const newUsers = users
.filter(({userID}) => !owners.some(({userID:ownerID})=> ownerID === userID))
console.log(newUsers)

You can just use a single .filter() function, like so:
let owners = [{userID: "58c4d7ac",username: "John.Doe",firstName: "John",lastName: "Doe",email: "John.Doe#acme.com"},{userID: "68c4d7ac",username: "User2.Name2",firstName: "User2",lastName: "Name2",email: "dh#acme.com"}];
let users = [{userID: "58c4d7ac",username: "John.Doe",firstName: "John",lastName: "Doe",email: "John.Doe#acme.com"},{userID: "68c4d7ac",username: "User2.Name2",firstName: "User2",lastName: "Name2",email: "dh#acme.com"},{userID: "88c4d7ac",username: "User3.Name3",firstName: "User3",lastName: "Name3",email: "dh#acme.com"}];
let newUsersItems = users.filter(user => owners.findIndex(owner => owner.userID === user.userID) === -1);
console.log(newUsersItems)
You can just use a single .filter() function, like so:

You can use the reduce function
const diff = users.reduce((acc, user) => {
if(!owners.find(owner => owner.id === user.id){
acc.push(user);
}
return acc;
}, []);
to avoid use the find() function every loop you can store owner ids in a array with the map an just use includes()
const ownersIds = owners.map(owner => owner.id);
const diff = users.reduce((acc, user) => {
if(!ownersIds.includes(user.id){
acc.push(user);
}
return acc;
}, []);

You can use the function some or find, this approach uses the function find
let owners = [ { userID: "58c4d7ac", username: "John.Doe", firstName: "John", lastName: "Doe", email: "John.Doe#acme.com" }, { userID: "68c4d7ac", username: "User2.Name2", firstName: "User2", lastName: "Name2", email: "dh#acme.com" } ],
users = [ { userID: "58c4d7ac", username: "John.Doe", firstName: "John", lastName: "Doe", email: "John.Doe#acme.com" }, { userID: "68c4d7ac", username: "User2.Name2", firstName: "User2", lastName: "Name2", email: "dh#acme.com" }, { userID: "88c4d7ac", username: "User3.Name3", firstName: "User3", lastName: "Name3", email: "dh#acme.com" } ],
result = users.filter(({userID}) => !owners.find(o => o.userID === userID));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

javascript function to get list of objects and return new key with new value in new array

for privacy I explain simple example . function that get personal list and return new key with new
value like below:
personalList = [{
firstName: "elena",
lastName: "zakharova"
},
{
firstName: "alex",
lastName: "farmanda"
},
{
firstName: "mike",
lastName: "kenan"
}]
output should be like this:
desiredList =[{fullname :'elena zakhrova'},{fullname :'alex farmanda'}, fullname : 'mike kenan']
tried some ways but unfortunately did not get answer
Any solutions would be appreciated.
You can use map
const personalList = [{
firstName: "elena",
lastName: "zakharova"
},
{
firstName: "alex",
lastName: "farmanda"
},
{
firstName: "mike",
lastName: "kenan"
}]
console.log(personalList.map(({firstName,lastName}) => ({fullName: `${firstName} ${lastName}`})))
Here's How I Would Solve This Using For Loop. You Can Also Use Maps Like How #R4ncid Has Explained
var desiredlist = []
var personalList = [{
firstName: "elena",
lastName: "zakharova"
},
{
firstName: "alex",
lastName: "farmanda"
},
{
firstName: "mike",
lastName: "kenan"
}]
for (i=0;i<personalList.length;i++){
desiredlist.push({"fullname":personalList[i].firstName + " " + personalList[i].lastName})}
console.log(desiredlist)

Best way to replace item in object?

I will fetch this data, the data is always diffrent. Sometimes admin exist sometimes not.
What is the best way to remove all the values in admin and add new value, if admin key exist?
const apiFetched = [{
persons: {
firstName: "John",
lastName: "Doe",
},
admin: {
firstName: "Jeff",
lastName: "Pan",
}
}]
If admin key exist I would like to replace everything in admin and add these:
{ firstName: "Alan", lastName: "Jack" }
like this:
const apiFetched = [{
persons: {
firstName: "John",
lastName: "Doe",
},
admin: {
firstName: "Alan",
lastName: "Jack",
},
}];
iterate the array via forEach
for each item which features an own admin property ...
assign to this item's admin property/object another object like superUser which can feature equally named and/or additional properties ...
in the 1st case, key-value pairs will be replaced/overwritten,
in the 2nd case the admin object would aggregate/accumulate new entries.
const apiFetched = [{
persons: {
firstName: "John",
lastName: "Doe",
},
admin: {
firstName: "Jeff",
lastName: "Pan",
},
}];
const superUser = { firstName: "Alan", lastName: "Jack" };
apiFetched
.forEach(item => {
if (item.hasOwnProperty('admin')) {
Object.assign(item.admin, superUser);
}
});
console.log({ apiFetched });
.as-console-wrapper { min-height: 100%!important; top: 0; }
Here you go:
const apiFetched = [{
persons: {
firstName: "John",
lastName: "Doe",
},
admin: {
firstName: "Jeff",
lastName: "Pan",
}
}]
apiFetched.forEach(i => {
if (i.admin) {
i.admin = {
firstname: "Alan",
lastname: "Jack"
}
}
})
console.log(apiFetched)

Javascript get array of objects from array of objects in array of objects

I have an array with registrations, inside this array is an array of students.
Now I want an array of all the students with only firstName, lastName and email.
registrations array
[
0: {
date: "2019-04-08T13:51:10.215Z"
onlyVAT: false,
students: [
0: {
email: "ben#test.be",
firstName: "Bennn",
lastName: "test",
phone: "0898989"
...
}
]
}
]
What I have so far:
this.registrations.map(
registration => registration.students.map(
student => { return {
firstName: student.firstName,
lastName: student.lastName,
email: student.email
}}
)
);
but this returns an array of arrays
0: [
0: {firstName: "Bennn", lastName: "test", email: "ben#test.be"},
1: ...
]
what I want is an array of (partial) student objects
[
0: {firstName: "Bennn", lastName: "test", email: "ben#test.be"},
1: ...
]
ofcourse I could just loop and push to a new array but that's not what I want
Use flat() or flatMap(). Example:
const newArr = registrations.map(
registration => registration.students.map(
student => { return {
firstName: student.firstName,
lastName: student.lastName,
email: student.email
}}
)
).flat();
console.log(newArr);
Just use flatMap.
this.registrations.flatMap(...);
Or use reduce:
this.registrations.map(...).reduce((a, c) => [...a, c], []);
Here is a solution with two Array.reduce() functions and Object.values(), which will ensure that the output will contain only unique emails (in the example input I have two identical emails ben4#test.be):
const registrations = [
{
date: '2019-04-08T13:51:10.215Z',
onlyVAT: false,
students: [
{
email: 'ben#test.be',
firstName: 'Bennn',
lastName: 'test',
phone: '0898989'
},
{
email: 'ben2#test.be',
firstName: 'Bennn2',
lastName: 'test2',
phone: '0898989'
}
]
},
{
date: '2019-05-08T13:51:10.215Z',
onlyVAT: false,
students: [
{
email: 'ben3#test.be',
firstName: 'Bennn3',
lastName: 'test3',
phone: '0898989'
},
{
email: 'ben4#test.be',
firstName: 'Bennn4',
lastName: 'test4',
phone: '0898989'
},
{
email: 'ben4#test.be',
firstName: 'Bennn4',
lastName: 'test4',
phone: '0898989'
}
]
}
];
const result = registrations.reduce((res, {students}) => ({
...res,
...students.reduce((res2, {email, firstName, lastName}) => ({
...res2,
[email]: {email, firstName, lastName}
}), {})
}), {});
console.log(Object.values(result));
The result is obtained in such form since students is a field inside an object in the array.
You could simply apply a combination of concat and ... spread operator on the result obtained from map to get the final array of students.
const studentsMapped = this.registrations.map(
registration => registration.students.map(
student => { return {
firstName: student.firstName,
lastName: student.lastName,
email: student.email
}}
)
);
const students = [].concat(...studentsMapped);

How to extract values from an array and store them inside another array? [duplicate]

This question already has answers here:
Changing the key name in an array of objects?
(11 answers)
Closed 3 years ago.
I have this array that I get from the serve:
dataFromServer = [
{
created_date: "02/10/2019"
date_of_birth: "01/01/2000"
email: "test#test.com"
first_name: "test"
last_name: "test"
mobile_phone: "999-999-9999"
registration_id: "3344"
},
{
created_date: "02/10/2015"
date_of_birth: "01/01/1980"
email: "test2#test2.com"
first_name: "test2"
last_name: "test2"
mobile_phone: "111-222-333"
registration_id: "123"
}
]
and I have to put it in another array to get rid of the "_" in between each property. So this is what I'm doing:
const newArray = []
dataFromServer.foreach(obj => {
newArray.push(
{
lastName: obj.last_name,
firstName: obj.first_name,
dateOfBirth: obj.date_of_birth,
registrationId: obj.registration_id,
createdDate: obj.created_date,
email: obj.email,
mobile_phone: obj.mobile_phone
});
});
Is there a better/ clear way in pure javascript (maybe using destructuring) or using Lodash? Thank a lot!
Yes, you can use map, perhaps (with ES2015+) with an arrow function:
const newArray = dataFromServer.map(obj => ({
lastName: obj.last_name,
firstName: obj.first_name,
dateOfBirth: obj.date_of_birth,
registrationId: obj.registration_id,
createdDate: obj.created_date,
email: obj.email,
mobile_phone: obj.mobile_phone
}));
Live Example:
const dataFromServer = [
{
created_date: "02/10/2019",
date_of_birth: "01/01/2000",
email: "test#test.com",
first_name: "test",
last_name: "test",
mobile_phone: "999-999-9999",
registration_id: "3344"
},
{
created_date: "02/10/2015",
date_of_birth: "01/01/1980",
email: "test2#test2.com",
first_name: "test2",
last_name: "test2",
mobile_phone: "111-222-333",
registration_id: "123"
}
];
const newArray = dataFromServer.map(obj => ({
lastName: obj.last_name,
firstName: obj.first_name,
dateOfBirth: obj.date_of_birth,
registrationId: obj.registration_id,
createdDate: obj.created_date,
email: obj.email,
mobile_phone: obj.mobile_phone
}));
console.log(newArray);
.as-console-wrapper {
max-height: 100% !important;
}
const newArray = dataFromServer.map(obj => ({
lastName: obj.last_name,
firstName: obj.first_name,
dateOfBirth: obj.date_of_birth,
registrationId: obj.registration_id,
createdDate: obj.created_date,
email: obj.email,
mobile_phone: obj.mobile_phone
}));

How to filter an array of objects for case insensitive matches from any object key

I have this sample code here, and I am trying to filter matching objects without exploding the code complexity or performance:
This code here filters matches based on one explicitly defined key and it's not case insensitive.
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)
THE GOAL:
I want it to match case-insensitive
I want it to return matches from any key
I want it to find using contains not exact match
Something like this:
// 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' },
I have scoured the documentations related to Array.filter() and I can definitely make solutions that involve Array.reduce() and looping over stuff with Object.keys(obj).forEach(), but I want to know if there is a concise, performant way to handle this kind of fuzzy search.
Something like this:
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)
[edit] This definitely works, but is it acceptable?
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)
Memory footprint optimized:
const found = people.filter((person) => JSON.stringify(person)
.toLowerCase()
.indexOf(searchString.toLowerCase()) !== -1
)
Converted to a function:
const fuzzyMatch = (collection, searchTerm) =>
collection.filter((obj) => JSON.stringify(obj)
.toLowerCase()
.indexOf(searchTerm.toLowerCase()) !== -1
)
console.log(fuzzyMatch(people, 'bob'))
There are some great answers in here; so far, I have selected this for my filtering needs:
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)
I made it able to support case sensitivity and to exclude specific keys.
If we assume that all properties are strings, then you might do in the following way
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)
Update: alternative solution with RegExp and more old-school :) but 2x faster
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))
You should give fusejs a shot. http://fusejs.io/
It has some interesting settings such as threshold, which allow some typo error (0.0 = perfect, 1.0 = match anything) and keys to specify any keys you want to search in.
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;
}
you need to filter the array, then filter each key in the objects to match a regular expression. This example breaks down the problem into single responsibility funcitons and connects them with functional concepts eg.
Performance tests are included, in chrome this is consistently faster than Dmitry's example. I have not tested any other browsers. This may be because of optimisations that chrome takes to allow the jit to process the script faster when the code is expressed as small single responsibility functions that only take one type of data as an input and one type of data as an output.
Due to the tests this takes around 4 seconds to load.
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>
You can also use Regular expressions with i modifier to perform case-insensitive matching and the method RegExp.prototype.test()
It is very convenient when you want to evaluate multiple object properties like:
new RegExp(searchString, 'i').test(
person.email || person.firstName || person.lastName
)
Code:
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)
If the entire matched object is expected result you can use 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);
You can use 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);
Array.prototype.filter() also works as well:
const includesQuery = (value) => {
return value['SOME_KEY'].toLowerCase().includes(query.toLowerCase());
}
const filtered = this.myArray.filter(includesQuery);
console.log(filtered);

Categories

Resources