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

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

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

save different elements in array by a condition

How can I save elements in an array based on a condition? In the following array, if I pass an age, it returns the array with the elements that meet that condition, but if I pass an age that does not exist or nothing happens when calling the function, how can I add all the elements of array1 to array2
const array1 = [
{
name: 'jose',
country: 'argentina',
age: 20
},
{
name: 'pedro',
country: 'brazil',
age: 18
},
{
name: 'andrea',
country: 'mexico',
age: 20
},
{
name: 'luis',
country: 'eu',
age: 19
},
{
name: 'nancy',
country: 'mexico',
age: 18
}
];
const getDatos = (age) => {
const array2 = array2.filter(data=> data.age === age);
console.log(array2 )
}
getDatos(20);
not sure if this is what you want, but you can check if the age is in the array using Array.prototype.some(MDN documentation)
const getDatos = (age) => {
if (array1.some(data => data.age === age)) {
return array1.filter(data=> data.age === age);
} else {
return array1;
}
}
EDIT: Based on Sebastian Simons comment, this approach it's better
const getDatos = (age) => {
const results = array1.filter(data => data.age === age);
return results.length > 0 ? results : array1;
}
You can use the ternary operator ? to check the length of array returned by filter. If any matches are found, return them, otherwise return all records.
const all = [
{ name: 'jose', country: 'argentina', age: 20 },
{ name: 'pedro', country: 'brazil', age: 18 },
{ name: 'andrea', country: 'mexico', age: 20 },
{ name: 'luis', country: 'eu', age: 19 },
{ name: 'nancy', country: 'mexico', age: 18 }
];
const getDatos = (age) => {
let matches = all.filter(data => data.age === age);
return (matches.length) ? matches : all;
}
console.log(getDatos(20)); // jose & andrea
console.log(getDatos(99)); // all records

How to compare and filter objects of array based on property value?

I have the following data structure:
persons: [
{ name: 'Joe', age: 20 },
{ name: 'Alex', age: 24 },
{ name: 'Joe', age: 34 },
{ name: 'Bob', age: 19 },
{ name: 'Alex', age: 56 },
]
I want to get the oldest person-object for each existing name. So the result of this example would be:
filteredPersons: [
{ name: 'Joe', age: 34 },
{ name: 'Bob', age: 19 },
{ name: 'Alex', age: 56 },
]
How can I achieve this? Note that the number of different names is not fixed.
You could take a Map and collect older ages for same names.
This soultion feature a function which compares two objects (or one object and a possible undefined) and if truthy and b.age is greater then a.age, it returns b, otherwise a.
At the end, only the values of the map are taken as result set.
const
older = (a, b) => b?.age > a.age ? b : a,
persons = [{ name: 'Joe', age: 20 }, { name: 'Alex', age: 24 }, { name: 'Joe', age: 34 }, { name: 'Bob', age: 19 }, { name: 'Alex', age: 56 }],
result = Array.from(persons.reduce((m, o) => m.set(
o.name,
older(o, m.get(o.name))
), new Map).values());
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
To do that in a single pass, you may employ Array.prototype.reduce() building up the Map that will have name as a key and store maximum age together with name as a value-object.
Once the Map is ready, you may extract its values with Map.prototype.values():
const src = [{name:'Joe',age:20},{name:'Alex',age:24},{name:'Joe',age:34},{name:'Bob',age:19},{name:'Alex',age:56},],
result = [...src
.reduce((acc, {name, age}) => {
const match = acc.get(name)
match ?
match.age = Math.max(age, match.age) :
acc.set(name, {name,age})
return acc
}, new Map)
.values()
]
console.log(result)
.as-console-wrapper{min-height:100%;}
Simply reduce the array and for each person in the array, check if the item has been encountered before, if so keep the oldest one, otherwise just keep the current object:
let results = persons.reduce((acc, person) => { // for each person in persons
if(!acc[person.name] || acc[person.name].age < person.age) { // if this person has never been encountered before (acc[person.name]) or if the already encountered one is younger (acc[person.name].age < person.age)
acc[person.name] = person; // store the current person under the name
}
return acc;
}, Object.create(null)); // Object.create(null) instead of {} to create a prototypeless object
This will return an object containing the oldest persons in this format { name: person, name: person, ... }. If you want to get them as an array, call Object.values like so:
let arrayResults = Object.values(results);
Demo:
let persons = [{ name: 'Joe', age: 20 }, { name: 'Alex', age: 24 }, { name: 'Joe', age: 34 }, { name: 'Bob', age: 19 }, { name: 'Alex', age: 56 }];
let results = persons.reduce((acc, person) => {
if(!acc[person.name] || acc[person.name].age < person.age) {
acc[person.name] = person;
}
return acc;
}, Object.create(null));
let arrayResults = Object.values(results);
console.log("results:", results);
console.log("arrayResults:", arrayResults);
Hope this is more understandable for you.
const persons = [
{ name: 'Joe', age: 20 },
{ name: 'Alex', age: 24 },
{ name: 'Joe', age: 34 },
{ name: 'Bob', age: 19 },
{ name: 'Alex', age: 56 },
]
let personsObj = {}, mxPersons = []
persons.forEach(person => {
if (personsObj[person.name] == undefined) {
personsObj[person.name] = person.age
} else {
personsObj[person.name] = Math.max(person.age, personsObj[person.name])
}
})
for (const [key, value] of Object.entries(personsObj)) {
mxPersons.push({
name: key,
age: value
})
}
console.log(mxPersons)
The oldest people per name can be obtained by first grouping all people based on their name, then take the oldest person of each group.
This answer does introduce two helper functions groupBy and maxBy, which add some overhead but are really usefull in general.
const people = [
{ name: 'Joe', age: 20 },
{ name: 'Alex', age: 24 },
{ name: 'Joe', age: 34 },
{ name: 'Bob', age: 19 },
{ name: 'Alex', age: 56 },
];
const oldestPeople = Array
.from(groupBy(people, person => person.name).values())
.map(people => maxBy(people, person => person.age));
console.log(oldestPeople);
function groupBy(iterable, fn) {
const groups = new Map();
for (const item of iterable) {
const key = fn(item);
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push(item);
}
return groups;
}
function maxBy(iterable, fn) {
let max, maxValue;
for (const item of iterable) {
const itemValue = fn(item);
if (itemValue <= maxValue) continue;
[max, maxValue] = [item, itemValue];
}
return max;
}

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

Filter JavaScript array adding/removing condition one by one

I want to filter array of objects with multiple search criteria one by one.
suppose first filter criteria is "x" and then apply "x+y" etc.wherever x or "x+y" matches with array of object,matched result to be displayed.
if user wants to remove "x" or "y",array should respond accordingly.
e.g :here x=mumbai and then y=25
var users = [{
name: 'nitin',
email: 'nitin#mail.com',
age: 25,
address: 'mumbai'
},
{
name: 'pawan',
email: 'pan#mail.com',
age: 35,
address: 'kolkata'
},
{
name: 'sudip',
email: 'sudip#mail.com',
age: 28,
address: 'mumbai'
}];
this.searchGridText = "";
this.users = this.users.filter((data) => {
const stringified = JSON.stringify(data);
const filtered = stringified.replace(/"\w+":/gm, '');
return (filtered.toLowerCase()
.indexOf(this.searchGridText.toLowerCase().trim()) !== -1);
});
when search criteria is "Mumbai",two record should return and then search criteria is 25,one record should return.if Mumbai search criteria removed,then only record matching to 25 should display.search criteria will be enter in text box and when enter key is hit,then filter will apply on array of objects.
Here is a way to do this via Array.filter, Array.every, Array.some and Object.values:
var users = [{ name: 'nitin', email: 'nitin#mail.com', age: 25, address: 'mumbai' }, { name: 'pawan', email: 'pan#mail.com', age: 35, address: 'kolkata' }, { name: 'sudip', email: 'sudip#mail.com', age: 28, address: 'mumbai' } ];
let findUsers = (arr, filters) => {
let fs = filters.split('+').map(x => x.trim().toLowerCase())
return arr.filter(x => {
let vs = Object.values(x)
return fs.every(f => vs.some(v => v.toString().trim().toLowerCase().includes(f)))
})
}
console.log(findUsers(users, 'mumbai'))
console.log(findUsers(users, 'mumbai + 25'))
console.log(findUsers(users, 'sudip'))
The idea is to filter and inside get the values of each object and iterate over them trying to match the passed to the function filters.
You can use your same idea but turning it into a function, and that is discriminating little by little by the characteristics that you are looking for
var users = [{
name: 'nitin',
email: 'nitin#mail.com',
age: 25,
address: 'mumbai'
},
{
name: 'pawan',
email: 'pan#mail.com',
age: 35,
address: 'kolkata'
},
{
name: 'sudip',
email: 'sudip#mail.com',
age: 28,
address: 'mumbai'
}
];
this.searchGridText = "mumbai 25";
let filters = searchGridText.split(" ");
let res = [];
for (let i = 0; i < filters.length; i++) {
res = filtereds(filters[i]);
users = res;
}
function filtereds(c) {
this.users = this.users.filter((data) => {
const stringified = JSON.stringify(data);
const filtered = stringified.replace(/"\w+":/gm, '');
//console.log(c);
return (filtered.toLowerCase()
.indexOf(c.toLowerCase().trim()) !== -1);
});
return this.users;
}
console.log(users);
No stringify, no regex and no problems:
function search(array, query) {
let filters = query.split("+").map(x => x.trim().toLowerCase())
return users.filter(user => {
for (let filter of filters) {
let match = false
for (let key in user) {
let value = user[key]
if (value && value.toString().toLowerCase().includes(filter)) {
match = true
break
}
}
if (!match) { return false }
}
return true
})
}
let users = [{
name: undefined,
email: 'nitin#mail.com',
age: 25,
address: 'mumbai'
}, {
name: 'pawan',
email: 'pan#mail.com',
age: 35,
address: 'kolkata'
}, {
name: 'sudip',
email: 'sudip#mail.com',
age: null,
address: 'mumbai'
}]
let result = search(users, "Mumbai + 25");
console.log(result)
Create a function that takes filter keys as an array and filter on those values. You can split the entered value into array using .split() and pass it to the function.
function searchJson(array, keys) {
return array.filter(item => {
const string = item && typeof item === 'object' && JSON.stringify(item).toLowerCase();
return keys.filter(key => string && string.includes(key.toString().toLowerCase())).length === keys.length;
})
}
var users = [{
name: 'nitin',
email: 'nitin#mail.com',
age: 25,
address: 'mumbai'
},
{
name: 'pawan',
email: 'pan#mail.com',
age: 35,
address: 'kolkata'
},
{
name: 'sudip',
email: 'sudip#mail.com',
age: 28,
address: 'mumbai'
}];
function searchJson(array, keys) {
return array.filter(item => {
const string = item && typeof item === 'object' && JSON.stringify(item).toLowerCase();
return keys.filter(key => string && string.includes(key.toString().toLowerCase())).length === keys.length;
})
}
console.log(searchJson(users, ['mumbai', 25]))

Categories