Related
Interface -
interface I {
name: string;
age: number;
city: string;
address?: string;
}
Arrays -
const arr1: I[] = [
{
name: "daniel",
age: 21,
city: 'NYC'
},
{
name: "kosta",
age: 28,
city: "NYC"
},
{
name: "yoav",
age: 28,
city: "NYC"
}
];
const arr2: I[] = [{
name: "daniel",
age: 21,
city: "NYC",
address: 'E. 43'
},
{
name: "simon",
age: 24,
city: "NYC",
address: 'E. 43'
},
{
name: "david",
age: 22,
city: "NYC",
address: 'E. 43'
},
{
name: "kosta",
age: 28,
city: "NYC",
address: 'E. 43'
}
];
Getting keys for the arrays -
const arr1Map: ReadonlyMap<string, string | undefined> = new Map(
arr1.map(
({
name, age, city, address
}) => [
`${name}|${age}|${city}`,
address
]
)
);
const arr2Map: ReadonlyMap<string, string | undefined> = new Map(
arr2.map(
({
name, age, city, address
}) => [
`${name}|${age}|${city}`,
address
]
)
);
Empty arrays -
let arr1Match: I[] = []
let arr1Unmatch: I[] = []
let arr2Match: I[] = []
let arr2Unmatch: I[] = []
What I need to do now is to campare all values in arr1 to arr2, if there is a match, store the match from arr1 in arr1Match and the match from arr2 in arr2Match. If there is an Unmatch I need to store the unmatch arr1 in arr1Unmatch and the unmatch from arr2 in arr2Unmatch.
And if there is a match I need to store the address from arr2 into arr1.
The desierd output -
arr1Match: [{ name: "daniel", age: 21, city: "NYC", address: 'E. 43' }, { name: "kosta", age: 28, city: "NYC", address: 'E. 43' } ]
arr2Match: [{ name: "daniel", age: 21, city: "NYC", address: 'E. 43' }, { name: "kosta", age: 28, city: "NYC", address: 'E. 43' }]
arr1Unmatch: [{ name: "yoav", age: 28, city: "NYC" }]
arr2Unmatch: [{ name: "simon", age: 24, city: "NYC", address: 'E. 43' }, { name: "david", age: 22, city: "NYC", address: 'E. 43' }]
The answer depends on some questions about your needs: What constitutes a match? If there's different data between the matches, what should be put in the match array? Should the arrays point to the original objects, or to copies of them?
Also, it looks like there is no difference between arr1Match and arr2Match, so those can be combined into one
Either way the solution would be to iterate over one array, and search for a match in the other array for every value. Any item that doesn't match will go to the unmatch arrays
// Replace with real match logic
const isMatch = <T>(a: T, b: T) => Math.random() < 0.5;
const getMatches = <T>(arrOne: T[], arrTwo: T[]) => {
const matches: T[] = [];
const arrOneUnmatches: T[] = [];
let arrTwoUnmatches: T[];
// Copying for comfortability's sake
const arrTwoCopy = [...arrTwo];
arrOne.forEach(item => {
// Find a match in array two
const arrTwoMatchIndex = arrTwoCopy.findIndex(arrTwoItem => isMatch(item, arrTwoItem));
if (arrTwoMatchIndex) {
matches.push(item);
// Remove it from arrTwoCopy, to maintain arrTwoUnmatches
arrTwoCopy.splice(arrTwoMatchIndex, 1);
} else {
// No match = go to arrOneUnmatches
arrOneUnmatches.push(item);
}
})
// Anyone left in arrTwoCopy didn't match anyone in arrOne, so they have no match
arrTwoUnmatches = arrTwoCopy;
return { matches, arrOneUnmatches, arrTwoUnmatches }
}
I'm trying to merge 2 arrays of objects where the resulting array should have only objects present in array 1 (selectedNames) but with one property from corresponding objects from array 2 (nameDetails). There's one matching (unique) property to match them:
const selectedNames = [
{
id: '11'
name: 'joe',
},
{
id: '22',
name: 'bill',
},
];
const nameDetails = [
{
nameId: '11',
salary: '23422',
location: 'New Jersey',
},
{
nameId: '33',
salary: '23424',
location: 'New York',
},
{
nameId: '22',
salary: '99999',
location: 'Boston',
},
{ nameId: '44',
salary: '323232',
location: 'Chicago',
},
];
The matching property is selectedNames.id === nameDetails.nameId. All entries in selectedNames will definitely be present in nameDetails (but not the other way round). The resulting array should look like that:
[
{
id: '11',
name: 'joe',
salary: '23422',
},
{
id: '22',
name: 'bill',
salary: '99999'
}
]
I'm a bit confused. I know it'll probably consist of .includes() and filter() and ... for merging? I'm not sure how to handle it.
Alternatively, which will probably be much easier, filter the nameDetails array to have only objects with nameId that exists (as id) in selectedNames.
I am a bit confused by your example result. For example where does id: '11111' come from?
Are you looking for something like this maybe?
const selectedNames = [
{
id: '11',
name: 'joe',
},
{
id: '22',
name: 'bill',
},
];
const nameDetails = [
{
nameId: '11',
salary: '23422',
location: 'New Jersey',
},
{
nameId: '33',
salary: '23424',
location: 'New York',
},
{
nameId: '22',
salary: '99999',
location: 'Boston',
},
{ nameId: '44',
salary: '323232',
location: 'Chicago',
},
];
const merged = selectedNames.map(n => {
n.salary = nameDetails.filter(d => n.id === d.nameId)[0].salary;
return n;
});
console.log(merged)
Note: This will change the original selectedNames by adding the salary from the corresponding entry in nameDetails.
Maybe this fits your needs:
selectedNames.map(({ id, name }) => {
let salary = nameDetails.find((nameItem) => nameItem.nameId === id).salary;
return { id, name, salary };
})
Will result in:
[
{
id: '11',
name: 'joe',
salary: '23422',
},
{
id: '22',
name: 'bill',
salary: '99999',
}
]
I have an array of object like that :
[{ name: 'Peter', age: '22', hobby: 'soccer', city: 'london' },
{ name: 'Mario', age: '30', hobby: 'bike', city: 'Paris' },
{ name: 'Peter', age: '22', hobby: 'soccer', city: 'Paris' },
{ name: 'Mario', age: '30', hobby: 'bike', city: 'Madrid' },
{ name: 'Rick', age: '28', hobby: 'tennis', city: 'Berlin' }],
And i would like group by the city, like below:
[{name: 'Peter', age: '22', hobby: 'soccer', cities: ['london', 'Paris']},
{name: 'Mario', age: '30', hobby: 'bike',cities: ['Paris', 'Madrid']},
{name: 'Rick', age: '28', hobby: 'tennis', cities: ['Berlin']}]
I try with this function by, I don't have the good array
let arrayPeople = [{ name: 'Peter', age: '22', hobby: 'soccer', city: 'london' },
{ name: 'Mario', age: '30', hobby: 'bike', city: 'Paris' },
{ name: 'Peter', age: '22', hobby: 'soccer', city: 'Paris' },
{ name: 'Mario', age: '30', hobby: 'bike', city: 'Madrid' },
{ name: 'Rick', age: '28', hobby: 'tennis', city: 'Berlin' }],
let groups = {};
for (let i = 0; i < arrayPeople.length; i++) {
let groupName = arrayPeople[i].city;
if (!groups[groupName]) {
groups[groupName] = [];
}
groups[groupName].push(arrayPeople[i].city);
}
let arrayGroupBy= [];
for (let groupName in groups) {
arrayGroupBy.push({ ...arrayPeople, cities: groups[groupName] });
}
How I can make my group by ?
Thank for your help.
You can use Array.reduce to process your input array, using the name, age and hobby as a key to create a grouping object, and adding the city for each key to an array in that object. You can then use Object.values to grab just the values from the grouping object into an array:
let arrayPeople = [{ name: 'Peter', age: '22', hobby: 'soccer', city: 'london' },
{ name: 'Mario', age: '30', hobby: 'bike', city: 'Paris' },
{ name: 'Peter', age: '22', hobby: 'soccer', city: 'Paris' },
{ name: 'Mario', age: '30', hobby: 'bike', city: 'Madrid' },
{ name: 'Rick', age: '28', hobby: 'tennis', city: 'Berlin' }];
let arrayGroupBy = Object.values(arrayPeople.reduce((c, { city, ...rest }) => {
let key = Object.values(rest).join('#');
c[key] = c[key] || { ...rest, city : [] };
c[key].city.push(city);
return c;
}, {}));
console.log(arrayGroupBy);
Note the above code is dependent on the properties in the objects being in the same order in each object (in the sample data, name, age, hobby). If this might not be the case, you will need to create the key using the named properties instead, for example:
let arrayPeople = [{ name: 'Peter', age: '22', hobby: 'soccer', city: 'london' },
{ age: '30', hobby: 'bike', name: 'Mario', city: 'Paris' },
{ name: 'Peter', hobby: 'soccer', city: 'Paris', age: '22' },
{ name: 'Mario', age: '30', hobby: 'bike', city: 'Madrid' },
{ name: 'Rick', age: '28', hobby: 'tennis', city: 'Berlin' }];
let arrayGroupBy = Object.values(arrayPeople.reduce((c, { name, age, hobby, city}) => {
let key = `${name}#${age}#${hobby}`;
c[key] = c[key] || { name, age, hobby, city : [] };
c[key].city.push(city);
return c;
}, {}));
console.log(arrayGroupBy);
Note also that all the above code is dependent on choosing a delimiter (I've used #) that will not occur in the name or hobby values. If there is no suitable delimiter character, then it is safer to use something like JSON.stringify to produce the key value e.g. for the second code block you would use:
let key = JSON.stringify([name, age, hobby]);
Your approach is pretty near to the correct solution. You are grouping persons by city. You have to turn around and group cities by person
let arrayPeople = [{ name: 'Peter', age: '22', hobby: 'soccer', city: 'london' },
{ name: 'Mario', age: '30', hobby: 'bike', city: 'Paris' },
{ name: 'Peter', age: '22', hobby: 'soccer', city: 'Paris' },
{ name: 'Mario', age: '30', hobby: 'bike', city: 'Madrid' },
{ name: 'Rick', age: '28', hobby: 'tennis', city: 'Berlin' }];
let groups = {};
for (let i = 0; i < arrayPeople.length; i++) {
let groupName = arrayPeople[i].name;
if (!groups[groupName]) {
groups[groupName] = {name: arrayPeople[i].name, age: arrayPeople[i].age, hobby: arrayPeople[i].hobby, cities: []};
}
groups[groupName].cities.push(arrayPeople[i].city);
}
console.log(Object.values(groups));
This is TypeScript so I'd probably come up with the type corresponding to the final array elements: that is, remove the city property and add a cities property:
type PersonCities = Omit<typeof arrayPeople[number], "city"> & { cities: string[] };
Then you want to put all your objects into a dictionary keyed by whatever you want the grouping condition to be (converted into a string):
const peopleCities: Record<string, PersonCities> = {};
for (let p of arrayPeople) {
const { city, ...person } = p;
const groupByKey = JSON.stringify([person.name, person.hobby, person.age]);
if (!(groupByKey in peopleCities)) {
peopleCities[groupByKey] = { ...person, cities: [] };
}
peopleCities[groupByKey].cities.push(city);
}
const arrayPeopleCities = Object.values(peopleCities);
Here we're making the grouping key a JSON string of the name, hobby, and age properties in an array. And some object rest/spread syntax to copy people properties around without too much redundancy.
Playground link to code
This question already has answers here:
Filter array of objects with another array of objects
(11 answers)
Closed 2 years ago.
I'm new to Javascript and I'm struggling with how to use the map, filter, find and other functions. I have two arrays of objects, and I wanted to filter the first one with the second.
const users = [
{ name: 'Anna', age: 22, gender: 'F' },
{ name: 'John', age: 25, gender: 'M' },
{ name: 'Mary', age: 27, gender: 'F' },
{ name: 'Joe', age: 30, gender: 'M' }
]
const filter = [
{ name: 'Anna' },
{ name: 'John' }
]
// Here is the expected result:
const expected_result = [
{ name: 'Anna', age: 22, gender: 'F' },
{ name: 'John', age: 25, gender: 'M' }
]
Does anyone know what is the best way to do this?
something like this?
const filteredUsers = users.filter(user=>filter.find(x=>(x.name==user.name)&&(x.age==user.age)&&(x.gender==user.gender));
what would be better is to give everyone a unique id.
const filteredUsers = users.filter(user=>filter.find(x=>(x.id==user.id));
"What is the best way" is a matter of opinion, but if you would have a large filter object, then it makes sense to first convert it to a temporary set:
function filterBy(users, filter) {
let set = new Set(filter.map(({name}) => name)); // for faster lookup
return users.filter(({name}) => set.has(name));
}
// demo
const users = [{name: 'Anna',age: 22,gender: 'F',},{name: 'John',age: 25,gender: 'M',},{name: 'Mary',age: 27,gender: 'F',},{name: 'Joe',age: 30,gender: 'M',},];
const filter = [{name: 'Anna',},{name: 'John',}];
console.log(filterBy(users, filter));
const users = [{
name: 'Anna',
age: 22,
gender: 'F',
},
{
name: 'John',
age: 25,
gender: 'M',
},
{
name: 'Mary',
age: 27,
gender: 'F',
},
{
name: 'Joe',
age: 30,
gender: 'M',
},
];
const filter = [{
name: 'Anna',
},
{
name: 'John',
},
];
const result = users.filter(value => {
const isExist = filter.findIndex(data => data.name === value.name);
return isExist === -1 ? false: true;
});
console.log(result)
I would like to compare a property of an object of two arrays of different lengths. If my condition is true (gender check) and then if that property matches then I would like to combine the properties of that object from both arrays and store it in a different array.
For example:
// array 1
var array1 = [{name: 'Jan', age: 19, category: {gender: 'Male'}}, {name: 'Suzy', age: 29, category: {gender: 'Female'}}, {name: 'Peter', age: 39, category: {gender: 'Male'}}, {name: 'Bart', age: 49, category: {gender: 'Male'}}, {name: 'John', age: 59, category: {gender: 'Male'}}];
// array 2
var array2 = [{name:'Kean', job: 'Technician'},{name:'Nick', job:'Mathematics'},{name: 'Jan', job: 'Tester'}, {name: 'Suzy', job:'Developer'}, {name: 'Peter', job: 'Scrum master'}]
Expected result:
var resultMale = [{name: 'Jan', age: 19,job: 'Tester'}, {name: 'Peter', age: 39, job: 'Scrum master'}];
var resultFemale = [{name: 'Suzy', age: 29, job:'Developer'}];
Below is my attempt just to show that I have been putting all my effort to find a solution myself. I have changed all the functions and variable names
this.
xxxxxxxx.getContractsForRules().then(res => {
// res.xxxxxx.forEach(function (obj) {
// if(obj.contract.product.xxxxxxx=== 'xxxxxxx') {
// console.log(this.xxxxxx.xx);
// for(let i=0; i < this.xxxxxxx.length; i++) {
// if(obj.contract.accountNumber === this.xxxxxxxx[i].ibanNumber) {
// this.currentAccount = {
// accountNumber: res.xxxxx[i].contract.accountNumber,
// accountName: res.xxxxx[i].contract.customer.xxxxxx
// };
// this.xxxxxxx.push(this.xxxxxx);
// }
// };
// }
// });
this.result = res.contractList.filter(item => this.allCurrentAccounts.);
if(res.xxxx[i].contract.xxxxx=== this.xxxxx[i].ibanNumber) {
this.savingAccount = {
accountNumber: xxxx.xxxx[i].contract.accountNumber,
accountName: res.xxxxx[i].contract.customer.xxxxx
};
this.xxxxx.push(this.xxxxx);
}
});
this.test();
}
What you finally need is an Intersection of both the arrays. So, you could do the following -
var array1 = [{ name: 'Jan', age: 19, category: { gender: 'Male' } }, { name: 'Suzy', age: 29, category: { gender: 'Female' } }, { name: 'Peter', age: 39, category: { gender: 'Male' } }, { name: 'Bart', age: 49, category: { gender: 'Male' } }, { name: 'John', age: 59, category: { gender: 'Male' } }];
var array2 = [{ name: 'Kean', job: 'Technician' }, { name: 'Nick', job: 'Mathematics' }, { name: 'Jan', job: 'Tester' }, { name: 'Suzy', job: 'Developer' }, { name: 'Peter', job: 'Scrum master' }];
// Empty arrays to contain final intersection array for both male & females
var resultMale = [], resultFemale = [];
/* now looping over both arrays to traverse all the elements from them */
// iterating over first array
array1.forEach(x => {
// iterating over second array
array2.forEach(y => {
// collect the objects only if the name attribute matches in both
if (x.name == y.name) {
// push into male array if gender is male else push into female array
if (x.category && x.category['gender'] == 'Male') {
resultMale.push({
name: x.name,
age: x.age,
job: y.job
});
} else if (x.category && x.category['gender'] == 'Female') {
resultFemale.push({
name: x.name,
age: x.age,
job: y.job
});
}
}
});
});
console.log(resultMale);
console.log(resultFemale);
Note - this can be optimized to reduce the time complexity.