Javascript multiple filters array - javascript

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

Related

Filtering an object in an array requiring 2 criteria

What would be the best way of filtering out an object in an array based on 2 factors, I thought a simple && operator would work but I was mistaken.
{
email: 'email#email.com',
accounts: [
{ name: 'Bob', country: 'UK' },
{ name: 'Chris', country: 'USA' },
{ name: 'Bob', country: 'USA' },
]
}
My original filter would just filter out based on accounts.name != 'Bob' however this can be problematic as there could be 2 Bobs with different countries.
let filterOut = result.accounts.filter(function (element) {
return element.name != 'Bob' && element.country != 'UK';
});
How could I use filter (if that is even the best option here) to achive the below output:
[
{ name: 'Chris', country: 'USA' },
{ name: 'Bob', country: 'USA' },
]
You could use an OR (||) to filter
data.accounts.filter(f => f.name != "Bob" || f.country == 'USA');
let data = {
email: 'email#email.com',
accounts: [
{ name: 'Bob', country: 'UK' },
{ name: 'Chris', country: 'USA' },
{ name: 'Bob', country: 'USA' },
]
}
let filtered = data.accounts.filter(f => f.name != "Bob" || f.country == 'USA');
console.log(filtered)
You are filtering out every value that is both not 'Bob' and not 'UK'.
Here are two possible solutions. The right answer probably depends on exactly what it means to filter "based on 2 factors".
const input = {
email: 'email#email.com',
accounts: [
{ name: 'Bob', country: 'UK' },
{ name: 'Chris', country: 'USA' },
{ name: 'Bob', country: 'USA' },
],
};
// Filter out every value with either name 'Bob' or country 'UK'.
const outputV1 = input.accounts.filter(v => v.name !== 'Bob' || v.country !== 'UK');
console.log(outputV1);
// Filter out every value with name 'Bob' and country 'UK'.
const outputV2 = input.accounts.filter(v => `${v.name}-${v.country}` !== 'Bob-UK');
console.log(outputV2);

How to get all objects and count booleans (JS)

Let it be like this for example:
let data = {
first: {
surname: 'Miller',
age: 37,
gender: 'male',
stealer: true
},
second: {
surname: 'Connor',
age: 28,
gender: 'male',
stealer: true
},
third: {
surname: 'Smith',
age: 13,
gender: 'female',
stealer: false
}
}
How can I get everything at once and count booleans of false, without the need to manually enter each one individually?
You can use Object.values to retrieve the values as an array and Array.prototype.reduce to count:
let data = { first: { surname: 'Miller', age: 37, gender: 'male', stealer: true }, second: { surname: 'Connor', age: 28, gender: 'male', stealer: true }, third: { surname: 'Smith', age: 13, gender: 'female', stealer: false } };
const result = Object.values(data).flatMap(el => Object.values(el)).reduce((acc, el) => acc + (el === false), 0);
console.log(result);

Coding Meetup #9 - Higher-Order Functions Series - Is the meetup age-diverse?

I am currently trying to complete Coding Meetup #9 on CodeWars.
How can I complete this only using for loops and if/else statements?
Here is the CodeWars Kata question:
You will be given an array of objects (associative arrays in PHP)
representing data about developers who have signed up to attend the
next coding meetup that you are organising.
Your task is to return: true if developers from all of the following
age groups have signed up: teens, twenties, thirties, forties,
fifties, sixties, seventies, eighties, nineties, centenarian (at least
100 years young). false otherwise.
For example, given the following input array:
var list = [
{ firstName: 'Harry', lastName: 'K.', country: 'Brazil', continent: 'Americas', age: 19, language: 'Python' },
{ firstName: 'Kseniya', lastName: 'T.', country: 'Belarus', continent: 'Europe', age: 29, language: 'JavaScript' },
{ firstName: 'Jing', lastName: 'X.', country: 'China', continent: 'Asia', age: 39, language: 'Ruby' },
{ firstName: 'Noa', lastName: 'A.', country: 'Israel', continent: 'Asia', age: 40, language: 'Ruby' },
{ firstName: 'Andrei', lastName: 'E.', country: 'Romania', continent: 'Europe', age: 59, language: 'C' },
{ firstName: 'Maria', lastName: 'S.', country: 'Peru', continent: 'Americas', age: 60, language: 'C' },
{ firstName: 'Lukas', lastName: 'X.', country: 'Croatia', continent: 'Europe', age: 75, language: 'Python' },
{ firstName: 'Chloe', lastName: 'K.', country: 'Guernsey', continent: 'Europe', age: 88, language: 'Ruby' },
{ firstName: 'Viktoria', lastName: 'W.', country: 'Bulgaria', continent: 'Europe', age: 98, language: 'PHP' },
{ firstName: 'Piotr', lastName: 'B.', country: 'Poland', continent: 'Europe', age: 128, language: 'JavaScript' }
];
Your function should return true as there is at least one developer
from each age group
Here is my code:
function isAgeDiverse(list) {
for (let i = 0; i < list.length; i++) {
if (list[i].age >= 18) {
return false;
}
}
return true;
}
Naive but working approach:
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
function isAgeDiverse(list) {
requiredAgeGroups = [1, 2, 3, 4, 5, 6, 7, 8, 9]
availableAgeGroups = [];
list.forEach(el => {
availableAgeGroups.push(Math.floor(el.age / 10));
});
availableAgeGroups = availableAgeGroups.filter(onlyUnique);
for (let i = 1; i < 10; i++ ){
if (availableAgeGroups.indexOf(i) == -1){
return false;
}
}
availableAgeGroups = availableAgeGroups.filter((i) => i > 9);
return availableAgeGroups.length > 0;
}

search order for array of objects in find method

I have next code:
var users = [
{
name: 'Mark',
email: 'mark#mail.com',
age: 28,
address: 'England',
val: true
},
{
name: 'John',
email: 'johnson#mail.com',
age: 25,
address: 'USA'
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
address: 'England'
}];
let res = users.find(
user => (user.name === 'John' || user.name === 'Tom'
|| user.val === true),
);
console.log(res);
How I can make search by 'name' as top priority, because now I am getting object with name Mark as result, but I need object with name John if it exists or Tom and if no names found search by val.
Try
let res = users.find(
user => (user.name === 'John' || user.name === 'Tom'
|| (!user.name && user.val === true)),
);
You shouldn't use Array.find method. It'd return only 1 value. You can use a for...of loop and have two arrays returned from it as below:
let prio1= [], prio2 = [];
for (const user of users) {
if (user.name === 'John' || user.name === 'Tom') prio1.push(user);
if (user.val) prio2.push(user);
}
let res = prio1.length ? prio1 : prio2;
You are close, change .find() to .filter() and it outputs you all users that match that criteria. I made a typo Johnk so it dont find John because of the wrong name and because he doesnt have val
var users = [
{
name: 'Mark',
email: 'mark#mail.com',
age: 28,
address: 'England',
val: true
},
{
name: 'Johnk',
email: 'johnson#mail.com',
age: 25,
address: 'USA'
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
address: 'England'
}];
let res = users.filter(
user => (user.name === 'John' || user.name === 'Tom'
|| user.val === true),
);
console.log(res);
I think what you're looking for is something like the following:
var users = [
{
name: "Mark",
email: "mark#mail.com",
age: 28,
address: "England",
val: true
},
{
name: "John",
email: "johnson#mail.com",
age: 25,
address: "USA"
},
{
name: "Tom",
email: "tom#mail.com",
age: 35,
address: "England"
}
];
function findUser(names) {
// use `Array.reduce` to loop through all names
// and find the first name in the array of `users`
var user = names.reduce((result, name) => {
if (result) {
return result;
}
return users.find(user => user.name === name) || null;
}, null);
// if user was not found, find a user based on the value of `val`
return user ? user : users.find(user => user.val);
}
var res1 = findUser(["John", "Tom"]);
var res2 = findUser(["Tom", "John"]);
var res3 = findUser(["Jeff"]);
console.log(res1);
console.log(res2);
console.log(res3);
References:
Array.reduce - MDN
let userbyName = users.find(user => (user.name === 'xyz')); //Search by name
if(userbyName == null)
{
let userbyVal = users.find(user => ( user.val=== 'true')); //Search by Value
}
This will work for both cases , put true/false as per you need
try this, it will return by val if no name is in the record
var users = [
{
name: 'Mark',
email: 'mark#mail.com',
age: 28,
address: 'England',
val: true
},
{
name: 'John',
email: 'johnson#mail.com',
age: 25,
address: 'USA'
},
{
name: 'Tom',
email: 'tom#mail.com',
age: 35,
address: 'England'
}];
function srch(name,val){
return users.filter(x=> x.name==name? x:(x.val==val?x:null))
}
console.log(srch("Mark",true))

JavaScript array re structure

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

Categories