I have this array of objects:
const a = [
{
id: 1,
name: 'John',
role: 'admin'
},
{
id: 1,
name: 'John',
role: 'user'
},
{
id: 2,
name: 'Max',
role: 'user'
}
]
I would like to have a result like this, so having one object for id:1 and a merged array in role property:
const a = [
{
id: 1,
name: 'John',
role: ['admin', 'user']
},
{
id: 2,
name: 'Max',
role: 'user'
}
]
EDIT:
I am able to remove duplicates when I have just to properties in the object. In my case I don't know how to retrieve the name property using the following snippet:
const b = [...new Set(a.map(d => d.id))].map(obj => {
return {
id: obj,
data: a.filter(d => d.id === obj).map(d => d.role)
}
})
You could take an object for grouping and use an array for additional roles.
const
data = [{ id: 1, name: 'John', role: 'admin' }, { id: 1, name: 'John', role: 'user' }, { id: 2, name: 'Max', role: 'user' }],
result = Object.values(data.reduce((r, o) => {
if (!r[o.id]) r[o.id] = { ...o };
else r[o.id].role = [].concat(r[o.id].role, o.role);
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It can be done very simply with a reducer:
const a = [
{
id: 1,
name: 'John',
role: 'admin'
},
{
id: 1,
name: 'John',
role: 'user'
},
{
id: 2,
name: 'Max',
role: 'user'
}
]
const b = a.reduce((acc, el)=>{
const existingEl = acc.find(accEl=>accEl.id === el.id)
if(existingEl) existingEl.role.push(el.role)
// a very inelegant way of building a shallow copy with
// a bit of a data structure change
else acc.push({id: el.id, name: el.name, role:[el.role]})
return acc
}, [])
console.log(b)
give this a try
const a = [
{
id: 1,
name: 'John',
role: 'admin'
},
{
id: 1,
name: 'John',
role: 'user'
},
{
id: 2,
name: 'Max',
role: 'user'
}
]
const newArr = a.reduce((acc, val) => {
const findIndex = acc.findIndex(f => f.id === val.id);
if (findIndex > -1) {
if ((typeof acc[findIndex].role === 'string')) {
acc[findIndex].role = [acc[findIndex].role, val.role]
} else {
acc[findIndex].role.push(val.role)
}
} else {
acc.push(val)
}
return acc
}, []);
console.log(newArr)
You can iterate over each item in your input, storing its data on an object keyed by the item's id property. Using a Set to collect the roles during iteration ensures that no duplicates will exist in the end result:
function mergeRoles (users) {
const merged = {};
for (const {id, name, role} of users) {
(merged[id] ??= {id, name, role: new Set([role])}).role.add(role);
}
return Object.values(merged).map(user => ({...user, role: [...user.role]}));
}
const input = [
{ id: 1, name: 'John', role: 'admin' },
{ id: 1, name: 'John', role: 'user' },
{ id: 2, name: 'Max', role: 'user' },
];
const result = mergeRoles(input);
console.log(result);
For problems like this I usually turn the array into an object dictionary to merge all the duplicates, then convert the dictionary back to an array:
const a = [{
id: 1,
name: 'John',
role: 'admin'
},
{
id: 1,
name: 'John',
role: 'user'
},
{
id: 2,
name: 'Max',
role: 'user'
}
];
// Merge duplicates using object dictionary.
let itemsById = {};
for (let item of a) {
if (!itemsById[item.id]) {
// Id not seen yet.
item.role = [item.role];
itemsById[item.id] = item;
} else {
// Duplicate Id.
itemsById[item.id].role.push(item.role);
}
}
// Convert object dictionary back to array.
let newArray = [];
for (const id in itemsById) {
let item = itemsById[id];
if (item.role.length == 1) {
item.role = item.role[0];
}
newArray.push(item);
}
console.log(newArray);
I have an array of objects in the format below and would like to transform it into a new array of objects using a property as a key. The key should be unique. See shape of the object below
const mockedList = [
{
email: 'aaa#example.com',
id: '5052',
name: 'Java',
},
{
email: 'bbb#example.com',
id: '5053',
name: 'Python',
},
{
email: 'aaa#example.com',
id: '5054',
name: 'C#',
},
{
email: 'bbb#example.com',
id: '5055',
name: 'Javascript',
},
];
I would like to transform this and get an array of objects with keys and values in this format.
[
{
email: 'bbb#example.com',
languages: [
{
email: 'bbb#example.com',
id: '5055',
name: 'Javascript',
},
{
email: 'bbb#example.com',
id: '5053',
name: 'Python',
},
]
},
{
email: 'aaa#example.com',
languages: [
{
email: 'aaa#example.com',
id: '5052',
name: 'Java',
},
{
email: 'aaa#example.com',
id: '5054',
name: 'C#',
},
]
}
]
I've tried using map-reduce
const result = mockedList.reduce((r, a) => {
r[a.email] = r[a.email] || [];
r[a.email].push(a);
return r;
}, Object.create(null));
But did not get the right shape of data
You can do:
const mockedList = [{email: 'aaa#example.com',id: '5052',name: 'Java',},{email: 'bbb#example.com',id: '5053',name: 'Python',},{email: 'aaa#example.com',id: '5054',name: 'C#',},{ email: 'bbb#example.com', id: '5055', name: 'Javascript' },]
const mockedListHash = mockedList.reduce((a, c) => {
a[c.email] = a[c.email] || { email: c.email, languages: [] }
a[c.email].languages.push(c)
return a
}, {})
const result = Object.values(mockedListHash)
console.log(result)
In case you want to clean the repeated emails within languages:
const mockedList = [{email: 'aaa#example.com',id: '5052',name: 'Java',},{email: 'bbb#example.com',id: '5053',name: 'Python',},{email: 'aaa#example.com',id: '5054',name: 'C#',},{ email: 'bbb#example.com', id: '5055', name: 'Javascript' },]
const mockedListHash = mockedList.reduce((a, c) => {
a[c.email] = a[c.email] || { email: c.email, languages: [] }
a[c.email].languages.push({
id: c.id,
name: c.name,
})
return a
}, {})
const result = Object.values(mockedListHash)
console.log(result)
Here is another option with simple for loop
// Array
const mockedList = [
{
email: 'aaa#example.com',
id: '5052',
name: 'Java'
},
{
email: 'bbb#example.com',
id: '5053',
name: 'Python'
},
{
email: 'aaa#example.com',
id: '5054',
name: 'C#'
},
{
email: 'bbb#example.com',
id: '5055',
name: 'Javascript'
}
];
// Set new object
const newObj = {};
// Use regular loop
for(const el of mockedList) {
// Use email as key
// If key already exist, add info
// to it's languages array
if(newObj[el.email]) newObj[el.email].languages.push(el);
else newObj[el.email] = {
email: el.email,
languages: [el]
}
}
// Test
console.log(newObj);
// If you need just array of objects,
// without email as key, then transform it
const newArr = Object.keys(newObj).map((key) => newObj[key]);
// Test
console.log(newArr);
I want to simplify an array of objects. Let's assume that I have following array:
var users = [{
name: 'John',
email: 'johnson#mail.com',
age: 25,
address: 'USA'
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
address: 'England'
},
{
name: 'Mark',
email: 'mark#mail.com',
age: 28,
address: 'England'
}];
And filter object:
var filter = {address: 'England', name: 'Mark'};
For example i need to filter all users by address and name, so i do loop through filter object properties and check it out:
function filterUsers (users, filter) {
var result = [];
for (var prop in filter) {
if (filter.hasOwnProperty(prop)) {
//at the first iteration prop will be address
for (var i = 0; i < filter.length; i++) {
if (users[i][prop] === filter[prop]) {
result.push(users[i]);
}
}
}
}
return result;
}
So during first iteration when prop - address will be equal 'England' two users will be added to array result (with name Tom and Mark), but on the second iteration when prop name will be equal Mark only the last user should be added to array result, but i end up with two elements in array.
I have got a little idea as why is it happening but still stuck on it and could not find a good solution to fix it. Any help is appreciable. Thanks.
You can do like this
var filter = {
address: 'England',
name: 'Mark'
};
var users = [{
name: 'John',
email: 'johnson#mail.com',
age: 25,
address: 'USA'
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
address: 'England'
},
{
name: 'Mark',
email: 'mark#mail.com',
age: 28,
address: 'England'
}
];
users= users.filter(function(item) {
for (var key in filter) {
if (item[key] === undefined || item[key] != filter[key])
return false;
}
return true;
});
console.log(users)
If you know the name of the filters, you can do it in a line.
users = users.filter(obj => obj.name == filter.name && obj.address == filter.address)
Another take for those of you that enjoy succinct code.
NOTE: The FILTER method can take an additional this argument, then using an E6 arrow function we can reuse the correct this to get a nice one-liner.
var users = [{name: 'John',email: 'johnson#mail.com',age: 25,address: 'USA'},
{name: 'Tom',email: 'tom#mail.com',age: 35,address: 'England'},
{name: 'Mark',email: 'mark#mail.com',age: 28,address: 'England'}];
var query = {address: "England", name: "Mark"};
var result = users.filter(search, query);
function search(user){
return Object.keys(this).every((key) => user[key] === this[key]);
}
// |----------------------- Code for displaying results -----------------|
var element = document.getElementById('result');
function createMarkUp(data){
Object.keys(query).forEach(function(key){
var p = document.createElement('p');
p.appendChild(document.createTextNode(
key.toUpperCase() + ': ' + result[0][key]));
element.appendChild(p);
});
}
createMarkUp(result);
<div id="result"></div>
Here is ES6 version of using arrow function in filter. Posting this as an answer because most of us are using ES6 these days and may help readers to do filter in advanced way using arrow function, let and const.
const filter = {
address: 'England',
name: 'Mark'
};
let users = [{
name: 'John',
email: 'johnson#mail.com',
age: 25,
address: 'USA'
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
address: 'England'
},
{
name: 'Mark',
email: 'mark#mail.com',
age: 28,
address: 'England'
}
];
users= users.filter(item => {
for (let key in filter) {
if (item[key] === undefined || item[key] != filter[key])
return false;
}
return true;
});
console.log(users)
users.filter(o => o.address == 'England' && o.name == 'Mark')
Much better for es6. or you can use || (or) operator like this
users.filter(o => {return (o.address == 'England' || o.name == 'Mark')})
Can also be done this way:
this.users = this.users.filter((item) => {
return (item.name.toString().toLowerCase().indexOf(val.toLowerCase()) > -1 ||
item.address.toLowerCase().indexOf(val.toLowerCase()) > -1 ||
item.age.toLowerCase().indexOf(val.toLowerCase()) > -1 ||
item.email.toLowerCase().indexOf(val.toLowerCase()) > -1);
})
Using Array.Filter() with Arrow Functions we can achieve this using
users = users.filter(x => x.name == 'Mark' && x.address == 'England');
Here is the complete snippet
// initializing list of users
var users = [{
name: 'John',
email: 'johnson#mail.com',
age: 25,
address: 'USA'
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
address: 'England'
},
{
name: 'Mark',
email: 'mark#mail.com',
age: 28,
address: 'England'
}
];
//filtering the users array and saving
//result back in users variable
users = users.filter(x => x.name == 'Mark' && x.address == 'England');
//logging out the result in console
console.log(users);
Improving on the good answers here, below is my solution:
const rawData = [
{ name: 'John', email: 'johnson#mail.com', age: 25, address: 'USA' },
{ name: 'Tom', email: 'tom#mail.com', age: 35, address: 'England' },
{ name: 'Mark', email: 'mark#mail.com', age: 28, address: 'England' }
]
const filters = { address: 'England', age: 28 }
const filteredData = rawData.filter(i =>
Object.entries(filters).every(([k, v]) => i[k] === v)
)
I think this might help.
const filters = ['a', 'b'];
const results = [
{
name: 'Result 1',
category: ['a']
},
{
name: 'Result 2',
category: ['a', 'b']
},
{
name: 'Result 3',
category: ['c', 'a', 'b', 'd']
}
];
const filteredResults = results.filter(item =>
filters.every(val => item.category.indexOf(val) > -1)
);
console.log(filteredResults);
Dynamic filters with AND condition
Filter out people with gender = 'm'
var people = [
{
name: 'john',
age: 10,
gender: 'm'
},
{
name: 'joseph',
age: 12,
gender: 'm'
},
{
name: 'annie',
age: 8,
gender: 'f'
}
]
var filters = {
gender: 'm'
}
var out = people.filter(person => {
return Object.keys(filters).every(filter => {
return filters[filter] === person[filter]
});
})
console.log(out)
Filter out people with gender = 'm' and name = 'joseph'
var people = [
{
name: 'john',
age: 10,
gender: 'm'
},
{
name: 'joseph',
age: 12,
gender: 'm'
},
{
name: 'annie',
age: 8,
gender: 'f'
}
]
var filters = {
gender: 'm',
name: 'joseph'
}
var out = people.filter(person => {
return Object.keys(filters).every(filter => {
return filters[filter] === person[filter]
});
})
console.log(out)
You can give as many filters as you want.
In lodash,
_.filter(users,{address: 'England', name: 'Mark'})
In es6,
users.filter(o => o.address == 'England' && o.name == 'Mark')
You'll have more flexibility if you turn the values in your filter object into arrays:
var filter = {address: ['England'], name: ['Mark'] };
That way you can filter for things like "England" or "Scotland", meaning that results may include records for England, and for Scotland:
var filter = {address: ['England', 'Scotland'], name: ['Mark'] };
With that setup, your filtering function can be:
const applyFilter = (data, filter) => data.filter(obj =>
Object.entries(filter).every(([prop, find]) => find.includes(obj[prop]))
);
// demo
var users = [{name: 'John',email: 'johnson#mail.com',age: 25,address: 'USA'},{name: 'Tom',email: 'tom#mail.com',age: 35,address: 'England'},{name: 'Mark',email: 'mark#mail.com',age: 28,address: 'England'}];var filter = {address: ['England'], name: ['Mark'] };
var filter = {address: ['England'], name: ['Mark'] };
console.log(applyFilter(users, filter));
If you want to put multiple conditions in filter, you can use && and || operator.
var product= Object.values(arr_products).filter(x => x.Status==status && x.email==user)
A clean and functional solution
const combineFilters = (...filters) => (item) => {
return filters.map((filter) => filter(item)).every((x) => x === true);
};
then you use it like so:
const filteredArray = arr.filter(combineFilters(filterFunc1, filterFunc2));
and filterFunc1 for example might look like this:
const filterFunc1 = (item) => {
return item === 1 ? true : false;
};
We can use different operators to provide multiple condtion to filter the array in the following way
Useing OR (||) Operator:
const orFilter = [{a:1, b: 3}, {a:1,b:2}, {a: 2, b:2}].filter(d => (d.a !== 1 || d.b !== 2))
console.log(orFilter, 'orFilter')
Using AND (&&) Operator:
const andFilter = [{a:1, b: 3}, {a:1,b:2}, {a: 2, b:2}].filter(d => (d.a !== 1 && d.b !== 2))
console.log(andFilter, 'andFilter')
functional solution
function applyFilters(data, filters) {
return data.filter(item =>
Object.keys(filters)
.map(keyToFilterOn =>
item[keyToFilterOn].includes(filters[keyToFilterOn]),
)
.reduce((x, y) => x && y, true),
);
}
this should do the job
applyFilters(users, filter);
My solution, based on NIKHIL C M solution:
let data = [
{
key1: "valueA1",
key2: "valueA2",
key3: []
},{
key1: "valueB1",
key2: "valueB2"
key3: ["valuesB3"]
}
];
let filters = {
key1: "valueB1",
key2: "valueB2"
};
let filteredData = data.filter((item) => {
return Object.entries(filters).every(([filter, value]) => {
return item[filter] === value;
//Here i am applying a bit more logic like
//return item[filter].includes(value)
//or filter with not exactly same key name like
//return !isEmpty(item.key3)
});
});
A question I was in the middle of answering got (properly) closed as duplicate of this. But I don't see any of the answers above quite like this one. So here's one more option.
We can write a simple function that takes a specification such as {name: 'mike', house: 'blue'}, and returns a function that will test if the value passed to it matches all the properties. It could be used like this:
const where = (spec, entries = Object .entries (spec)) => (x) =>
entries .every (([k, v]) => x [k] == v)
const users = [{name: 'John', email: 'johnson#mail.com', age: 25, address: 'USA'}, {name: 'Mark', email: 'marcus#mail.com', age: 25, address: 'USA'}, {name: 'Tom', email: 'tom#mail.com', age: 35, address: 'England'}, {name: 'Mark', email: 'mark#mail.com', age: 28, address: 'England'}]
console .log ('Mark', users .filter (where ({name: 'Mark'})))
console .log ('England', users .filter (where ({address: 'England'})))
console .log ('Mark/England', users .filter (where ({name: 'Mark', address: 'England'})))
.as-console-wrapper {max-height: 100% !important; top: 0}
And if we wanted to wrap the filtering into a single function, we could reuse that same function, wrapped up like this:
const where = (spec, entries = Object .entries (spec)) => (x) =>
entries .every (([k, v]) => x [k] == v)
const filterBy = (spec) => (xs) =>
xs .filter (where (spec))
const users = [{name: 'John', email: 'johnson#mail.com', age: 25, address: 'USA'}, {name: 'Mark', email: 'marcus#mail.com', age: 25, address: 'USA'}, {name: 'Tom', email: 'tom#mail.com', age: 35, address: 'England'}, {name: 'Mark', email: 'mark#mail.com', age: 28, address: 'England'}]
console .log ('Mark/England', filterBy ({address: "England", name: "Mark"}) (users))
.as-console-wrapper {max-height: 100% !important; top: 0}
(Of course that last doesn't have to be curried. We could change that so that we could call it with two parameters at once. I find this more flexible, but YMMV.)
Keeping it as a separate function has the advantage that we could then reuse it, in say, a find or some other matching situation.
This design is very similar to the use of where in Ramda (disclaimer: I'm one of Ramda's authors.) Ramda offers the additional flexibility of allowing arbitrary predicates instead of values that have to be equal. So in Ramda, you might write something like this instead:
filter (where ({
address: equals ('England')
age: greaterThan (25)
}) (users)
It's much the same idea, only a bit more flexible.
If the finality of you code is to get the filtered user, I would invert the for to evaluate the user instead of reducing the result array during each iteration.
Here an (untested) example:
function filterUsers (users, filter) {
var result = [];
for (i=0;i<users.length;i++){
for (var prop in filter) {
if (users.hasOwnProperty(prop) && users[i][prop] === filter[prop]) {
result.push(users[i]);
}
}
}
return result;
}
with the composition of some little helpers:
const filter = {address: 'England', name: 'Mark'};
console.log(
users.filter(and(map(propMatches)(filter)))
)
function propMatches<T>(property: string, value: any) {
return (item: T): boolean => item[property] === value
}
function map<T>(mapper: (key: string, value: any, obj: T) => (item:T) => any) {
return (obj: T) => {
return Object.keys(obj).map((key) => {
return mapper(key, obj[key], obj)
});
}
}
export function and<T>(predicates: ((item: T) => boolean)[]) {
return (item: T) =>
predicates.reduce(
(acc: boolean, predicate: (item: T) => boolean) => {
if (acc === undefined) {
return !!predicate(item);
}
return !!predicate(item) && acc;
},
undefined // initial accumulator value
);
}
This is an easily understandable functional solution
let filtersObject = {
address: "England",
name: "Mark"
};
let users = [{
name: 'John',
email: 'johnson#mail.com',
age: 25,
address: 'USA'
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
address: 'England'
},
{
name: 'Mark',
email: 'mark#mail.com',
age: 28,
address: 'England'
}
];
function filterUsers(users, filtersObject) {
//Loop through all key-value pairs in filtersObject
Object.keys(filtersObject).forEach(function(key) {
//Loop through users array checking each userObject
users = users.filter(function(userObject) {
//If userObject's key:value is same as filtersObject's key:value, they stay in users array
return userObject[key] === filtersObject[key]
})
});
return users;
}
//ES6
function filterUsersES(users, filtersObject) {
for (let key in filtersObject) {
users = users.filter((userObject) => userObject[key] === filtersObject[key]);
}
return users;
}
console.log(filterUsers(users, filtersObject));
console.log(filterUsersES(users, filtersObject));
This is another method i figured out, where filteredUsers is a function that returns the sorted list of users.
var filtersample = {address: 'England', name: 'Mark'};
filteredUsers() {
return this.users.filter((element) => {
return element['address'].toLowerCase().match(this.filtersample['address'].toLowerCase()) || element['name'].toLowerCase().match(this.filtersample['name'].toLowerCase());
})
}
const users = [{
name: 'John',
email: 'johnson#mail.com',
age: 25,
address: 'USA'
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
address: 'England'
},
{
name: 'Mark',
email: 'mark#mail.com',
age: 28,
address: 'England'
}
];
const filteredUsers = users.filter(({ name, age }) => name === 'Tom' && age === 35)
console.log(filteredUsers)
Using lodash and not pure javascript
This is actually quite simple using lodash and very easy to add/modify filters.
import _ from 'lodash';
async getUsersWithFilter(filters) {
const users = yourArrayOfSomethingReally();
// Some properties of the 'filters' object can be null or undefined, so create a new object without those undefined properties and filter by those who are defined
const filtersWithoutUndefinedValuesObject = _.omitBy(
filters,
_.isNil,
);
return _.filter(users, { ...filtersWithoutUndefinedValuesObject });
}
The omitBy function checks your filters object and removes any value that is null or undefined (if you take it out, the lodash.filter function wont return any result.
The filter function will filter out all the objects who's values don't match with the object you pass as a second argument to the function (which in this case, is your filters object.)
Why use this?
Well, assume you have this object:
const myFiltersObj = {
name: "Java",
age: 50
};
If you want to add another filter, just add a new property to the myFilterObj, like this:
const myFiltersObj = {
name: "Java",
email: 50,
country: "HND"
};
Call the getUsersWithFilter function, and it will work just fine. If you skip, let's say the name property in the object, the getUsersWithFilter function will filter by the email and country just fine.
Please check below code snippet with data you provided, it will return filtered data on the basis of multiple columns.
var filter = {
address: 'India',
age: '27'
};
var users = [{
name: 'Nikhil',
email: 'nikhil#mail.com',
age: 27,
address: 'India'
},
{
name: 'Minal',
email: 'minal#mail.com',
age: 27,
address: 'India'
},
{
name: 'John',
email: 'johnson#mail.com',
age: 25,
address: 'USA'
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
address: 'England'
},
{
name: 'Mark',
email: 'mark#mail.com',
age: 28,
address: 'England'
}
];
function filterByMultipleColumns(users, columnDataToFilter) {
return users.filter(row => {
return Object.keys(columnDataToFilter).every(propertyName => row[propertyName].toString().toLowerCase().indexOf(columnDataToFilter[propertyName].toString().toLowerCase()) > -1);
})
}
var filteredData = filterByMultipleColumns(users, filter);
console.log(filteredData);
Result :
[ { "name": "Nikhil", "email": "nikhil#mail.com", "age": 27, "address": "India" }, { "name": "Minal", "email": "minal#mail.com", "age": 27, "address": "India" } ]
Please check below link which can used with just small changes
Javascript filter array multiple values – example
const data = [{
realName: 'Sean Bean',
characterName: 'Eddard “Ned” Stark'
}, {
realName: 'Kit Harington',
characterName: 'Jon Snow'
}, {
realName: 'Peter Dinklage',
characterName: 'Tyrion Lannister'
}, {
realName: 'Lena Headey',
characterName: 'Cersei Lannister'
}, {
realName: 'Michelle Fairley',
characterName: 'Catelyn Stark'
}, {
realName: 'Nikolaj Coster-Waldau',
characterName: 'Jaime Lannister'
}, {
realName: 'Maisie Williams',
characterName: 'Arya Stark'
}];
const filterKeys = ['realName', 'characterName'];
const multiFilter = (data = [], filterKeys = [], value = '') => data.filter((item) => filterKeys.some(key => item[key].toString().toLowerCase().includes(value.toLowerCase()) && item[key]));
let filteredData = multiFilter(data, filterKeys, 'stark');
console.info(filteredData);
/* [{
"realName": "Sean Bean",
"characterName": "Eddard “Ned” Stark"
}, {
"realName": "Michelle Fairley",
"characterName": "Catelyn Stark"
}, {
"realName": "Maisie Williams",
"characterName": "Arya Stark"
}]
*/
arr.filter((item) => {
if(condition)
{
return false;
}
return true;
});
I have a kids object that looks like the following:
const kids = {
name: 'john',
extra: {
city: 'London',
hobbies: [
{
id: 'football',
team: 'ABC',
},
{
id: 'basketball',
team: 'DEF',
},
],
},
};
and i have the following object that contains all sports and extra info for each.
const sports = [
{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
},
];
I want to get the list of all ids in the hobbies array and go through each of the sports items in the sports array, and found, add an extra field to that object available and give a value of true, so the result will look like:
const result = [
{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
available: true
},
];
by the way, here is my attempt:
const result = kids.extra.hobbies.map(a => a.id);
for (var key in sports) {
console.log(sports[key].name);
const foundIndex = result.indexOf(sports[key].name);
if ( foundIndex > -1) {
sports[key].available = true;
}
}
console.log(sports)
but this is too long... i am looking one liner looking code and robust logic.
This can be done many ways; however, an easy was is to divide the problem into two steps:
We can first flatten the kid's hobbies into an array by using the Array.map() function:
const hobbies = kids.extra.hobbies.map(hobby => hobby.id);
Then, we can iterate through the sports array and add an active property to any object which is present in the new hobbies array:
const result = sports.map(sport => {
if (hobbies.indexOf(sport.name) !== -1) {
sport.available = true;
}
return sport;
})
Complete Solution
const kids = {
name: 'john',
extra: {
city: 'London',
hobbies: [{
id: 'football',
team: 'ABC',
},
{
id: 'basketball',
team: 'DEF',
},
],
},
};
const sports = [{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
},
];
const hobbies = kids.extra.hobbies.map(hobby => hobby.id);
const result = sports.map(sport => {
if (hobbies.indexOf(sport.name) !== -1) {
sport.available = true;
}
return sport;
})
console.log(result);
Firstly, I would change my data structures to objects. Any time you have a list of things with unique ids, objects will make your life much easier than arrays. With that in mind, if you must use arrays, you could do the following:
const hobbies = kids.extra.hobbies
sports.forEach(s => s.available = hobbies.some(h => h.id === s.name))
Note that this mutates the original sports object (change to map for new), and also adds false/true instead of just true.
Build an array of the found sports first, then map while checking to see if the sports object's name is in it:
const kids = {name:'john',extra:{city:'London',hobbies:[{id:'football',team:'ABC',},{id:'basketball',team:'DEF',},],},}
const sports = [{name:'volleyball',coach:'tom',},{name:'waterpolo',coach:'jack',},{name:'swimming',coach:'kate',},{name:'football',coach:'sara',},];
const sportsInHobbies = kids.extra.hobbies.map(({ id }) => id);
const result = sports.map((sportObj) => {
const available = sportsInHobbies.includes(sportObj.name);
return available ? {...sportObj, available } : { ...sportObj };
});
console.log(result);