JavaScript array re structure - javascript

I have an array with student and parent addresses.
For example,
const users = [{
id: 1,
name: 'John',
email: 'johnson#mail.com',
age: 25,
parent_address: 'USA',
relationship:'mother'
},
{
id: 1,
name: 'John',
email: 'johnson#mail.com',
age: 25,
parent_address: 'Spain',
relationship:'father'
},
{
id: 2,
name: 'Mark',
email: 'mark#mail.com',
age: 28,
parent_address: 'France',
relationship:'father'
}
];
I'm trying to reformat this to the following result.
const list = [
{
id: 1,
name: 'John',
email: 'johnson#mail.com',
age: 25,
parent: [
{
parent_address: 'USA',
relationship:'mother'
},{
parent_address: 'Spain',
relationship:'father'
}
]
},
{
id: 2,
name: 'Mark',
email: 'mark#mail.com',
age: 28,
parent:[
{
parent_address: 'France',
relationship:'father'
}
]
}
];
So far I tried the following way. I'm not sure that is the right way or not.
const duplicateInfo = [];
for (var i = 0; i < user[0].length; i++) {
var parent = [];
if (duplicateInfo.indexOf(user[0][i].id) != -1) {
// Do duplicate stuff
} else {
// Do other
}
duplicateInfo.push(user[0][i].id);
}

One approach would be to use .reduce() with an object as an accumulator. For each id, you can store an associated object with a parents array which you can append to in your .reduce() callback whenever you encounter a new object with the same id. Then to get an array of objects from your object, you can call Object.values() on it
See example below:
const users = [{ id: 1, name: 'John', email: 'johnson#mail.com', age: 25, parent_address: 'USA', relationship: 'mother' }, { id: 1, name: 'John', email: 'johnson#mail.com', age: 25, parent_address: 'Spain', relationship: 'father' }, { id: 2, name: 'Mark', email: 'mark#mail.com', age: 28, parent_address: 'France', relationship: 'father' } ];
const res = Object.values(users.reduce((acc, {parent_address, relationship, ...r}) => { // use destructuring assignment to pull out necessary values
acc[r.id] = acc[r.id] || {...r, parents: []}
acc[r.id].parents.push({parent_address, relationship}); // short-hand property names allows us to use the variable names as keys
return acc;
}, {}));
console.log(res);
Since you mentioned you're new to JS, it may be easier to understand in a more imperative way (see code comments for details):
const users = [{ id: 1, name: 'John', email: 'johnson#mail.com', age: 25, parent_address: 'USA', relationship: 'mother' }, { id: 1, name: 'John', email: 'johnson#mail.com', age: 25, parent_address: 'Spain', relationship: 'father' }, { id: 2, name: 'Mark', email: 'mark#mail.com', age: 28, parent_address: 'France', relationship: 'father' } ];
const unique_map = {}; // create an object - store each id as a key, and an object with a parents array as its value
for(let i = 0; i < users.length; i++) { // loop your array object
const user = users[i]; // get the current object
const id = user.id; // get the current object/users's id
if(!(id in unique_map)) // check if current user's id is in the the object
unique_map[id] = { // add the id to the unique_map with an object as its associated value
id: id,
name: user.name,
email: user.email,
age: user.age,
parents: [] // add `parents` array to append to later
}
unique_map[id].parents.push({ // push the parent into the object's parents array
parent_address: user.parent_address,
relationship: user.relationship
});
}
const result = Object.values(unique_map); // get all values in the unique_map
console.log(result);

You could reduce the array and search for a user with the same id and add the parent information to it.
If the user is not found, add a new user to the result set.
const
users = [{ id: 1, name: 'John', email: 'johnson#mail.com', age: 25, parent_address: 'USA', relationship: 'mother' }, { id: 1, name: 'John', email: 'johnson#mail.com', age: 25, parent_address: 'Spain', relationship: 'father' }, { id: 2, name: 'Mark', email: 'mark#mail.com', age: 28, parent_address: 'France', relationship: 'father' }],
grouped = users.reduce((r, { parent_address, relationship, ...user }) => {
var temp = r.find(q => q.id === user.id );
if (!temp) r.push(temp = { ...user, parent: []});
temp.parent.push({ parent_address, relationship });
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Restructuring data like this is pretty common and Array.reduce() is designed for the task. It is a different way of viewing things and takes some getting used to, but after you write the code a few times it becomes second nature.
reduce() is called on an array and takes two parameters:
a function that will be called for each element in the array
the starting value
Your function then is called for each element with the the starting value for the first run or the return value from the previous function call for each subsequent run, along the array element, index into the original array, and the original array that reduce() was called on (the last two are usually ignored and rarely needed). It should return the object or whatever you are building up with the current element added, and that return value gets passed to the next call to your function.
For things like this I usually have an object to keep the unique keys (id for you), but I see you want an array returned. That's one line to map the object and keys to an array and it's more efficient to use the build-in object property mechanism instead of array.find() to see if you have added an id already.
const users = [{
id: 1,
name: 'John',
email: 'johnson#mail.com',
age: 25,
parent_address: 'USA',
relationship:'mother'
},
{
id: 1,
name: 'John',
email: 'johnson#mail.com',
age: 25,
parent_address: 'Spain',
relationship:'father'
},
{
id: 2,
name: 'Mark',
email: 'mark#mail.com',
age: 28,
parent_address: 'France',
relationship:'father'
}
];
let combined = users.reduce(
// function called for each element in the array
(previous, element) => {
// previous starts out as the empty object we pass as the second argument
// and will be the return value from this function for every other element
// create an object for the id on our 'previous' object if it doesn't exist,
// if it does exist we will trust the name, email, and age from the first
// instance
previous[element.id] = previous[element.id] || {
id: element.id,
name: element.name,
age: element.age,
parents: []
};
// now add parent
previous[element.id].parents.push({
parent_address: element.parent_address,
relationship: element.relationship
});
// return our updated object, which will be passed to the next call
// and eventually returned
return previous;
},
{} // initial value is an empty object, no ids yet
);
// transform object into array with elements in order by key
let list = Object.keys(combined).sort().map(key => combined[key]);
console.dir(list);

You need to iterate twice using the current method. The complexity is O(n^2). (for Loop + indexOf)
A better way is index the array and use the array key for duplication detection and search.
For example:
const map = {};
users.forEach(user => {
// Will return undefined if not exist
let existing = map[user.id];
if (!existing) {
// If not exist, create new
existing = {
id: user.id,
...
parents: [ {parent_address: user.parent_address, relationship: user.relationship ]
}
} else {
// Otherwise, update only parents field
// You can add other logic here, for example update fields if duplication is detected.
existing.parents.push({parent_address: user.parent_address, relationship: user.relationship ]
});
}
map[user.id] = existing;
})
// Convert the object to array
const list = map.values();

const users = [{
id: 1,
name: 'John',
email: 'johnson#mail.com',
age: 25,
parent_address: 'USA',
relationship:'mother'
},
{
id: 1,
name: 'John',
email: 'johnson#mail.com',
age: 25,
parent_address: 'Spain',
relationship:'father'
},
{
id: 2,
name: 'Mark',
email: 'mark#mail.com',
age: 28,
parent_address: 'France',
relationship:'father'
}
];
const updatedUsers = users.map(user => {
return {
id: user.id,
name: user.name,
email: user.email,
age: user.age,
parent: [{
relationship: user.relationship,
parent_address: user.parent_address,
}]
}
})
const list = updatedUsers.reduce((acc, user) => {
const findIndex = acc.findIndex(eachUser => eachUser.id === user.id && eachUser.email === user.email);
if (findIndex < 0) {
acc.push(user);
return acc;
} else {
acc[findIndex].parent.push(user.parent);
return acc;
}
}, []);
console.log(list)

You can use Map collection to store unique items and just populate it using filter:
const unique = new Map(users.map(u=>
[u.id, {...u, parent: [...users.filter(f => f.id == u.id)]}]));
console.log(Array.from(unique, ([k, v])=> v)
.map(s => ( { id: s.id, name: s.name, email: s.email, age:s.age, parent:s.parent })));
const users = [
{
id: 1,
name: 'John',
email: 'johnson#mail.com',
age: 25,
parent_address: 'USA',
relationship: 'mother'
},
{
id: 1,
name: 'John',
email: 'johnson#mail.com',
age: 25,
parent_address: 'Spain',
relationship: 'father'
},
{
id: 2,
name: 'Mark',
email: 'mark#mail.com',
age: 28,
parent_address: 'France',
relationship: 'father'
}
];
const unique = new Map(users.map(u=>
[u.id, {...u, parent: [...users.filter(f => f.id == u.id)]}]));
console.log(Array.from(unique, ([k, v])=> v).map(s => (
{ id: s.id, name: s.name, email: s.email, age:s.age, parent:s.parent })));

const users = [{
id: 1,
name: 'John',
email: 'johnson#mail.com',
age: 25,
parent_address: 'USA',
relationship:'mother'
},
{
id: 1,
name: 'John',
email: 'johnson#mail.com',
age: 25,
parent_address: 'Spain',
relationship:'father'
},
{
id: 2,
name: 'Mark',
email: 'mark#mail.com',
age: 28,
parent_address: 'France',
relationship:'father'
}
];
ids = new Map()
for (const user of users) {
var newuser;
if (ids.has(user.id)) {
newuser = ids.get(user.id);
} else {
newuser = {};
newuser.id = user.id;
newuser.name = user.name;
newuser.email = user.email;
newuser.age = user.age;
newuser.parent = [];
}
relationship = {};
relationship.parent_address = user.parent_address;
relationship.relationship = user.relationship;
newuser.parent.push(relationship)
ids.set(user.id, newuser);
}
list = [ ...ids.values() ];
list.forEach((u) => {
console.log(JSON.stringify(u));
});

Related

How to move similar profession people into newly created object (result) in javascript

I have one quick question, I want to move all similar professional people into a newly created object (result), I have written the code but all values are not moved.
const data = [
{
Name: 'Smith',
age: 25,
profession: 'Banker'
},
{
Name: 'Alex',
age: 28,
profession: 'IT'
},
{
Name: 'John',
age: 31,
profession: 'Banker'
},
{
Name: 'Harry',
age: 26,
profession: 'Nurse'
},
];
const result = {};
My code is here ...
const data = [ { Name: "Smith", age: 25, profession: "Banker" }, { Name: "Alex", age: 28, profession: "IT" }, { Name: "John", age: 31, profession: "Banker" }, { Name: "Harry", age: 26, profession: "Nurse" } ];
const result = {};
data.forEach(({ Name, age, profession }) => {
result[profession] = { Name, age };
});
console.log(result);
CodePen:
https://codepen.io/Sandy4405/pen/wvmLaJX
Expanding on my comment above, the inside of your forEach should be:
data.forEach(({ Name, age, profession }) => {
if (Array.isArray(result[profession])) {
result[profession].push({ Name, age })
} else {
result[profession] = [{ Name, age }]
}
});
You need to create a nested JSON, which means an array of objects for a similar profession. While iterating, create an array for each profession and use .push() to add the objects. The code would look like below
data.forEach(({Name,age,profession}) => {
result[profession] = result[profession] || [];
result[profession].push({Name,age});
});
Working Version:
const data = [{
Name: "Smith",
age: 25,
profession: "Banker"
},
{
Name: "Alex",
age: 28,
profession: "IT"
},
{
Name: "John",
age: 31,
profession: "Banker"
},
{
Name: "Harry",
age: 26,
profession: "Nurse"
}
];
const result = {};
data.forEach(({
Name,
age,
profession
}) => {
result[profession] = result[profession] || [];
result[profession].push({
Name,
age
});
});
console.log(result);
After the first person with a given profession is encountered, each subsequent person with the same profession will overwrite the previous one. You should instead create an array and push each of the people with the same profession to that array.
First, we'll check if the current profession has been encountered before.
If it hasn't, we'll create an empty array to hold all of the people with this profession.
if (!(profession in result)) {
result[profession] = []
}
Then, since the array is now guaranteed to exist for this profession, we can push to our new array the current person. The next time this profession is encountered, our first check will be skipped and we'll just push the next person onto the array.
result[profession].push({
Name,
age
})
Full example:
const data = [{
Name: "Smith",
age: 25,
profession: "Banker"
}, {
Name: "Alex",
age: 28,
profession: "IT"
}, {
Name: "John",
age: 31,
profession: "Banker"
}, {
Name: "Harry",
age: 26,
profession: "Nurse"
}]
const result = {}
data.forEach(({
Name,
age,
profession
}) => {
if (!(profession in result))
result[profession] = []
result[profession].push({
Name,
age
})
})
console.log(result)
As filipe said in his comment, Your current code is just assigning the values into an object which results in maintaining the single value against keys. To group the same profession values into a respective profession key, You have to do something like this :
const data = [ { Name: "Smith", age: 25, profession: "Banker" }, { Name: "Alex", age: 28, profession: "IT" }, { Name: "John", age: 31, profession: "Banker" }, { Name: "Harry", age: 26, profession: "Nurse" } ];
const result = {};
data.forEach(({ Name, age, profession }) => {
result[profession] ? result[profession].push({ Name, age }) : result[profession] = [{ Name, age }];
});
console.log(result);

how to select parent property in object javascript

how to put the user hobby property (in line 2) into the friend hobby property (in line 6)
const user = {
hobby: 'sports',
friends: [{
name: 'jhon',
age: '22',
get hobby () {
// how to return user hobby here?
}
}]
}
const user = {
hobby: 'sports',
friends: [{
name: 'jhon',
age: '22',
get hobby () {
return user.hobby
}
}]
}
user.friends[0].hobby // 'sports'
Or if you have a list of those objects and you want to add the getter to them:
const users = [
{
hobby: 'sports',
friends: [{ name: 'jhon', age: 22 }]
}, {
hobby: 'cooking',
friends: [{ name: 'jane', age: 23 }]
}
]
for (const u of users)
Object.defineProperty(u.friends[0], 'hobby', {
get() {
return u.hobby
}
})
users[0].friends[0].hobby // 'sports'
users[1].friends[0].hobby // 'cooking'

Push multiple objects to an already initialized, empty object array

People is my model, data is my new information, and the forEach is how I am trying to insert the new data into my model, but formatted to only the information I care about
people = [{name: '', age: 0}];
data = [
{id: '123', name: 'Bob', lastName: 'Guy', age: 40},
{id: '321', name: 'Michael', lastName: 'Park', age: 20},
]
data.forEach(person => {
people.push({
name: person.name,
age: person.age,
});
});
However, the result I get is this:
people = [
{name: '', age: 0},
{name: 'Bob', age: 40},
{name: 'Michael', age: 20}
];
I'm trying to have the object array look like this instead:
people = [
{name: 'Bob', age: 40},
{name: 'Michael', age: 20}
];
However, I would like to know if theres a way to do it without an extra line of code (like popping the first element), and if theres a way to do it in one command? If not, I am open to suggestions. Thank you!
You're using the original array and not only that but also you're mutating the array.
You can use the function Array.prototype.map in order to generate a new array with the desired data.
const people = [{name: '', age: 0}];
const data = [
{id: '123', name: 'Bob', lastName: 'Guy', age: 40},
{id: '321', name: 'Michael', lastName: 'Park', age: 20},
]
const result = data.map(person => ({
name: person.name,
age: person.age,
}));
console.log(result);
You can also keep the desired keys and by using the functions Array.prototype.map and Array.prototype.reduce you can build the expected result:
const model = ["name", "age"];
const data = [
{id: '123', name: 'Bob', lastName: 'Guy', age: 40},
{id: '321', name: 'Michael', lastName: 'Park', age: 20},
]
const result = data.map(person => model.reduce((r, m) => ({...r, [m]: person[m]}), {}), []);
console.log(result);
Just in case you need to implement different person models, you can dinamically create the objects like this
peopleModel = [{ name: "", age: 0 }];
data = [
{ id: "123", name: "Bob", lastName: "Guy", age: 40 },
{ id: "321", name: "Michael", lastName: "Park", age: 20 },
];
const keysArr = Object.keys(peopleModel[0]);
const totalKeys = keysArr.length;
const people = data.reduce((acc, personObj) => {
const obj = {};
keysArr.forEach((key) => {
if (personObj[key]) {
obj[key] = personObj[key];
}
});
acc.push(obj);
return acc;
}, []);
console.log(people);
/* logs [
{name: 'Bob', age: 40},
{name: 'Michael', age: 20}
];
*/
but if you need a different model like
peopleModel = [{ name: "", age: 0, lastName: "" }]
you will get for the log the following:
[
{name: 'Bob', age: 40, lastName: 'Guy'},
{name: 'Michael', age: 20, lastName: 'Park'}
];
that way you do not need to hardcode the keys

Using Javascript Array Properties to restrict a random output [duplicate]

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;
});

Javascript multiple filters array

My question is extension to this question javascript filter array multiple conditions
from that question if filter object is
{address: 'England', name: 'Mark'};
and array is
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'
}
];
so the answer is
[
{
"name": "Mark",
"email": "mark#mail.com",
"age": 28,
"address": "England"
}
]
which is absolutely fine but my question is array has to be filtered for the filter object properties value
for example my filter object will be {address: 'England', name: ''} now this has to filter the array for all names and address England
You'd use filter on users and every on the filter object's entries
const filter = {address: 'England', name: 'Mark'};
const res = users.filter(user =>
Object.entries(filter)
.every(([k,v]) => v === '' || user[k] === v)
);
console.log(res);
<script>
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'
}
];
</script>
From the example in the post you mention, just continue if the filter is blank
var filter = {address: 'England', name: ''}
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 (filter[key] == "") continue; // added this:
if (item[key] === undefined || item[key] != filter[key])
return false;
}
return true;
});
console.log(users)
You can use a combination of filter and every with some ternary logic to determine if the filter value is empty to get all.
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 filter1 = {address: 'England', name: 'Mark'};
var filter2 = {address: 'England', name: ''};
function findResults(input, filterObj){
return input.filter(
item => Object.keys(filterObj)
.every(r => filterObj[r].length
? item[r] == filterObj[r]
: true)
)
}
console.log('with address and name', findResults(users,filter1));
console.log('with address only', findResults(users,filter2));
If I understand your question correctly you need following output.
If this what you are looking for Array.filter should suffice your use case.
Take a look at the code sandbox where I have created a function filterByObj which takes arr, filterObj as arguments and returns given output for { address: "England", name: "" }
You need you filter for this case.
var filter1 = {
address: 'England',
name: 'Mark'
};
var filter2 = {
address: 'England',
name: ''
};
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'
}
];
function filterUser(arr, obj) {
return arr.filter(function(item) {
return (
(obj.address === '' || item.address === obj.address) &&
(obj.name === '' || item.name === obj.name)
);
});
}
console.log(filterUser(users, filter1));
console.log(filterUser(users, filter2));

Categories