I'm writing a small js code where I need to filter the data based on key passed. Here, the main issue is, the data is not consistent(please refer to my code sample).
var users = [{
name: 'paul',
job: 'engineer'
},
{
name: 'John',
job: 'Mechanic'
},
{
name: 'paul',
job: 'Mechanic'
},
{
name: 'George',
job: 'Plumber'
},
{
name: 'John'
},
];
filtersToApply = {
job: 'engineer'
};
returnFilteredList = (users, columnDataToFilter) => {
return users.filter((row) => {
return Object.keys(columnDataToFilter).every(
(propertyName) =>
row[propertyName]
.toString()
.toLowerCase()
.indexOf(columnDataToFilter[propertyName].toString().toLowerCase()) >
-1
);
});
};
console.log(JSON.stringify(returnFilteredList(users, filtersToApply)));
Here I get the error, 'coz, there is no job for the last JSON object in the array. how can I handle this?
You could get the entries of you filter conditions and check with Array#every or Array#some, depending on the need.
const
users = [{ name: 'paul', job: 'engineer' }, { name: 'John', job: 'Mechanic' }, { name: 'paul', job: 'Mechanic' }, { name: 'George', job: 'Plumber' }, { name: 'John' }],
filtersToApply = { job: 'engineer' },
filters = Object.entries(filtersToApply),
result = users.filter(user =>
filters.every(([k, v]) => (user[k] || '').toLowerCase() === v)
);
console.log(result);
If you have an array or only a sting, you need to compare each value and adjust the case in advance.
const
users = [{ name: 'paul', job: 'engineer' }, { name: 'John', job: ['Mechanic', 'Engineer'] }, { name: 'paul', job: 'Mechanic' }, { name: 'George', job: 'Plumber' }, { name: 'John' }],
filtersToApply = { job: 'engineer' },
filters = Object.entries(filtersToApply),
result = users.filter(user =>
filters.every(([k, v]) => []
.concat(user[k] || [])
.map(s => s.toLowerCase())
.includes(v)
)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Sometimes row[propertyName] will be undefined. You can use an Optional Chaining operator to avoid the errors:
return Object.keys(columnDataToFilter).every(
(propertyName) =>
row[propertyName]? //<--
.toString()
.toLowerCase()
.indexOf(columnDataToFilter[propertyName].toString().toLowerCase()) >
-1
);
return Object.keys(columnDataToFilter).every(
(propertyName) =>
row[propertyName]|| '' //you can add undefined keys to empty string.
.toString()
.toLowerCase()
.indexOf(columnDataToFilter[propertyName].toString().toLowerCase()) >
-1
);
Related
I cant figure out how to do this...
const arr1 = [{ name: 'peter' }, { name: 'sam', id: 1 }, { name: 'mark' }];
const arr2 = [{ name: 'sam' }, { name: 't' }, { name: 'george' }];
Desired outcome:
const arr2 = [{ name: 'sam', id: 1 }, { name: 't' }, { name: 'george' }];
If you want the previous item I would do this:
const arr1 = [{
name: 'peter'
}, {
name: 'sam',
id: 1
}, {
name: 'mark'
}];
const arr2 = [{
name: 'sam'
}, {
name: 't'
}, {
name: 'george'
}];
const result = arr2.map(item => {
const previousItem = arr1.find(i => i.name === item.name)
if (previousItem) {
return previousItem
}
return item
})
console.log(result);
However, if you want to combine the old and new data, I would recommend spreading the data together, like so:
const arr1 = [{
name: 'peter'
}, {
name: 'sam',
id: 1
}, {
name: 'mark'
}];
const arr2 = [{
name: 'sam'
}, {
name: 't'
}, {
name: 'george'
}];
const result = arr2.map(item => {
const previousItem = arr1.find(i => i.name === item.name)
if (previousItem) {
return {
...previousItem,
...item
}
}
return item
})
console.log(result);
Both allude to the same result here, but you would get different results if arr2's "Sam" object had an additional key "age" on it...
In this example, the second snippet would keep the "age" key because the spread (...) operation combines the two objects together.
You can try this.
const arr1 = [{ name: 'peter' }, { name: 'sam', id: 1 }, { name: 'mark' }];
const arr2 = [{ name: 'sam' }, { name: 't' }, { name: 'george' }];
const result = [];
const res1 = arr2.map((item, i) => {
let index = arr1.findIndex((x) => x.name === item.name);
if ( index > -1 )
result.push(arr1[index]);
else
result.push(item);
})
console.log(result);
let a =
[
{
Name: 'Josh',
Subject: ['Biology', 'Chemistry'],
},
{
Name: 'James',
Subject: ['Chemistry', 'Physics'],
},
{
Name: 'Mary',
Subject: ['Physics', 'Mathematics'],
},
]
const result = a.reduce((groupedSubject, person) => {
const Subject = person.Subject
if(groupedSubject[Subject]== null) groupedSubject[Subject] = []
groupedSubject[Subject].push(person)
return groupedSubject
}, {})
console.log(result)
I was able to group according to the subject, but subject is an array. how do i group it by individual subjects?
You need to iterate Sports array as well.
const
data = [{ Name: 'Ravindra', Sports: ['Chess', 'Cricket'] }, { Name: 'Ravi', Sports: ['Cricket', 'Football'] }, { Name: 'Rishabh', Sports: ['Table-Tennis', 'Football'] }],
result = data.reduce((groupedSports, person) => {
person.Sports.forEach(sport => {
groupedSports[sport] ??= [];
groupedSports[sport].push(person.Name);
})
return groupedSports;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can iterate over all sports and check if it already exists, if not, spread all current entries and add the new persons Name to the array.
let a = [{
Name: 'Ravindra',
Sports: ['Chess', 'Cricket'],
},
{
Name: 'Ravi',
Sports: ['Cricket', 'Football'],
},
{
Name: 'Rishabh',
Sports: ['Table-Tennis', 'Football'],
},
]
const result = a.reduce((groupedSports, person) => {
person.Sports.forEach((sport) => {
groupedSports[sport] = groupedSports?.[sport]
? [...groupedSports[sport], person.Name]
: [person.Name];
});
return groupedSports;
}, {});
console.log(result)
I need to fix this function, which must find two similar names in an array of objects.
I tried to do this, and it's working, but the test tells me that it should be just only one loop and one if
function searchByName() {
const values = [
{ name: 'Johny Walker', birthDate: '1995-12-17' },
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Viktor', birthDate: '1998-11-09' },
{ name: 'Andrew', birthDate: '2011-05-09' }
];
for (let obj of values) {
for (let elem of values) {
if (obj == elem)
continue;
if (elem.name === obj.name && elem.age === obj.age) {
console.log(obj);
break;
}
}
}
};
Here is the example that must come out:
[
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Andrew', birthDate: '2011-05-09' }
]
Just push names to the array and stop when there is a duplicate:
let hasDuplicates = [];
values.forEach((e, idx) => {
if(!hasDuplicates.includes(e.name))
if(idx !== values.length-1) { hasDuplicates.push(e.name); }
else { hasDuplicates = false; }
else { hasDuplicates = e; }
});
And then you can use that variable:
if(hasDuplicates) {...}
You can find the duplication count by name key using Array.reduce function.
And from the duplication result, you can filter the duplicated ones only and show them using Array.filter & Array.map.
const values = [
{ name: 'Johny Walker', birthDate: '1995-12-17' },
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Viktor', birthDate: '1998-11-09' },
{ name: 'Andrew', birthDate: '2011-05-09' }
];
let duplicates = values.reduce((acc, cur) => {
if (acc[cur.name]) {
acc[cur.name].push(cur.birthDate);
} else {
acc[cur.name] = [ cur.birthDate ];
}
return acc;
}, {});
duplicates = Object.entries(duplicates).filter(([name, birth]) => birth.length > 1).map(([name, birth]) => {
return birth.map((item) => ({
name,
birthDate: item
}));
});
console.log(duplicates);
You can use the reduce method to iterate over the array and push items into the array. Then we just filter the array by its length. So if the length is greater than 1, it means we have found a duplicate:
const result = values.reduce((a, c)=> {
a[c.name] = a[c.name] || {...c, items: []} ;
a[c.name].items.push(c);
return a;
}, { });
let result = Object.values(result).filter(f => f.items.length > 1).flatMap(s => s.items);
An example:
const values = [
{ name: 'Johny Walker', birthDate: '1995-12-17' },
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Viktor', birthDate: '1998-11-09' },
{ name: 'Andrew', birthDate: '2011-05-09' }
];
const result = values.reduce((a, c)=> {
a[c.name] = a[c.name] || {...c, items: []} ;
a[c.name].items.push(c);
return a;
}, { });
console.log(Object.values(result).filter(f => f.items.length > 1).flatMap(s => s.items));
I have an array like this:
[
{
0 : {
id: 'somevalue',
name: 'John Doe',
age: '20'
}
}
...
]
I would like to modify the array, for example to set the key to the id attribute like this:
[
{
somevalue : {
name: 'John Doe',
age: '20'
}
}
]
What would be the best way to achieve this. Thanks for your time.
You could destructure the object and take the wanted key out of the object. Then return new object with wanted value.
var array = [{ 0 : { id: 'somevalue', name: 'John Doe', age: '20' } }],
key = 'id',
result = array.map(({ 0: { [key]: k, ...o } }) => ({ [k]: o }));
console.log(result);
Assuming there is only one nested object, you can use the function map as follow:
let arr = [
{
0 : {
id: 'somevalue',
name: 'John Doe',
age: '20'
}
}
];
let result = arr.map(o => {
let [{id, ...rest}] = Object.values(o);
return {[id]: rest};
});
console.log(result);
I guess you have just more then 1 Object. You can try it like this.
let data = [
{
0 : {
id: 'somevalue',
name: 'John Doe',
age: '20'
}
}
]
let result = data.map((el, index) => {
let id = el[index].id;
delete el[index].id;
return { [id]: {...el[index]}}
})
console.log(result);
You can try this if the key is always '0' in the given object
const arr = [
{
0 : {
id: 'somevalue',
name: 'John Doe',
age: '20'
}
}
]
arr.map(item => {
const value = {...item[0]};
const key = value.id;
delete value.id;
return {
[key]: value
}
})
You can use map and reduce functions to achieve the desired output.
This solution will work even if you have more than one nested objects and if you have more properties in your nested objects, i.e. properties other than name and age
const arr = [
{
0: { id: "somevalue", name: "John Doe", age: "20" },
1: { id: "somevalue 2", name: "John Doe", age: "20", gender: 'male' }
},
{
0: { id: "somevalue 3", name: "John Doe", age: "20" },
1: { id: "somevalue 4", name: "John Doe", age: "20", gender: 'male' },
2: { id: "somevalue 5", name: "John Doe", age: "20" }
}
];
const res = arr.map((obj) => {
const v = Object.values(obj);
return v.reduce((acc, curr) => {
const {id, ...restProps } = curr;
acc[curr.id] = restProps;
return acc;
}, {});
});
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I would like to fuse Array.filter() function to remove duplicate objects
I am able to achieve in the case of string or integer arrays. But I am not able to achieve the same with array of objects as in the second case of names
const names = ['John', 'Paul', 'George', 'Ringo', 'John'];
let x = names => names.filter((v, i, arr) => arr.indexOf(v) === i);
console.log(x(names)); //[ 'John', 'Paul', 'George', 'Ringo' ]
const names = [
{ name: "John" },
{ name: "Paul" },
{ name: "George" },
{ name: "Ringo" },
{ name: "John" } ];
// returns the same original array
Could you please help?
Using Array#reduce() and a Map accumulator then spread the values() of the Map into array
const names = [
{ name: "John" },
{ name: "Paul" },
{ name: "George" },
{ name: "Ringo" },
{ name: "John" } ];
const unique = [... names.reduce((a,c)=>(a.set(c.name,c)),new Map).values()]
console.log(unique)
Use Array.reduce and Object.values
Iterate over the array and create an object with key as name and value as object from array. In case of objects with same name, the value will be overwritten in resultant object. Finally use Object.values to collect all the unique objects.
const names = [{ name: "John" },{ name: "Paul" },{ name: "George" },{ name: "Ringo" },{ name: "John" } ];
let result = Object.values(names.reduce((a,c) => Object.assign(a, {[c.name]:c}),{}));
console.log(result);
For tweaking - Plunker
const names = [
{ name: "John" },
{ name: "Paul" },
{ name: "George" },
{ name: "Ringo" },
{ name: "John" }
];
/* unique => Filter: Remove all duplicate items from an array. Works with plain objects as well, since we stringify each array item.
* #type public Function
* #name unique
* #return Function( item )
* #notes
*/
const unique = () => {
const seen = {};
return item => {
const json = JSON.stringify( item );
return seen.hasOwnProperty( json )
? false
: ( seen[ json ] = true );
};
};
const result = names.filter( unique() );
console.log( result );
You could use lodash's _uniqBy for this:
const names = [
{ name: "John" },
{ name: "Paul" },
{ name: "George" },
{ name: "Ringo" },
{ name: "John" } ];
const result = _uniqBy(names, 'name');
This can be done with the help of Sets as well
var names = [{ name: "John" },{ name: "Paul" },{ name: "George" },{ name: "Ringo" },{ name: "John" } ];
var result = Array.from(
names.reduce((s, d) => s.add(d.name), new Set)
, d => ({ name: d })
)
console.log(result)
Keith had a great suggestion to use findIndex with filter instead of indexOf. Object literals are always unique references, so we cannot compare them. We can however compare the name keys between the objects. We can do this with the aforementioned functions.
const names = [
{ name: "John" },
{ name: "Paul" },
{ name: "George" },
{ name: "Ringo" },
{ name: "John" }
];
console.log(names.filter(({name1}, i, a) => {
return i == a.findIndex(({name2}) => {
return name1 == name2;
});
});
const names = ['John', 'Paul', 'George', 'Ringo', 'John'];
function removeDups(names) {
let unique = {};
names.forEach(function(i) {
if(!unique[i]) {
unique[i] = true;
}
});
return Object.keys(unique);
}
removeDups(names); //'John', 'Paul', 'George', 'Ringo'