Check any two object in an array are Equal using multiple Conditions - javascript

In my angular project i have a DataSource like
[
{id: "1", name: "John", age: "23", gender:"M", Role: "Student"},
{id: "2", name: "Smith", age: "24", gender:"M", Role: "Teacher"},
{id: "3", name: "Jeff", age: "23", gender:"M", Role: "Student"},
{id: "4", name: "Ronald", age: "25", gender:"M", Role: "Teacher"},
{id: "5", name: "Ronak", age: "23", gender:"M", Role: "Student"}
]
I need to check if any two or more lines of data are same or not based on multiple conditions. Eg : If any two lines have same age and gender and Role then return their Names
So the Expected Output will be
[John,Jeff, Ronak]
Since they share the same age, gender and Role
else
[]
I tried to group the array, but I didn't find any solution to group it using multiple conditions, then tried nested loops, but seems inefficient and the results were not as expected.

You could take a joined key for the wanted properties, collenct the names for the group and return all names who are in a group with three or more items.
var data = [{ id: "1", name: "John", age: "23", gender: "M", Role: "Student" }, { id: "2", name: "Smith", age: "24", gender: "M", Role: "Teacher" }, { id: "3", name: "Jeff", age: "23", gender: "M", Role: "Student" }, { id: "4", name: "Ronald", age: "25", gender: "M", Role: "Teacher" }, { id: "5", name: "Ronak", age: "23", gender: "M", Role: "Student" }],
keys = ['age', 'gender', 'Role'],
groups = data.reduce((r, o) => {
const key = keys.map(k => o[k]).join('|');
r[key] = r[key] || [];
r[key].push(o.name);
return r;
} , {}),
result = Object.values(groups).flatMap(a => a.length >= 3 ? a : []);
console.log(result);
console.log(groups);

I would use a helper function to group the data appropriately, and pass it a function that generates a unique key from the important data. Something like this:
// Utility functions
const groupBy = (fn) => (xs) =>
xs .reduce ((a, x) => ({... a, [fn(x)]: [... (a [fn (x)] || []), x]}), {})
const getProp = (obj) => (prop) =>
obj [prop]
// Helper function
const makeKey = (props) => (record) =>
props .map (getProp (record)) .join('|')
// Main function
const multipleMatch = (props) => (data) =>
Object
.values (groupBy (makeKey(props)) (data))
.filter (arr => arr.length > 1)
// Demos
const data = [{id: "1", name: "John", age: "23", gender:"M", Role: "Student"}, {id: "2", name: "Smith", age: "24", gender:"M", Role: "Teacher"}, {id: "3", name: "Jeff", age: "23", gender:"M", Role: "Student"}, {id: "4", name: "Ronald", age: "25", gender:"M", Role: "Teacher"}, {id: "5", name: "Ronak", age: "23", gender:"M", Role: "Student"}]
console .log (multipleMatch (['age', 'gender', 'Role']) (data))
// Changing the age of one to get two different shared groups
const data2 = [{id: "1", name: "John", age: "23", gender:"M", Role: "Student"}, {id: "2", name: "Smith", age: "24", gender:"M", Role: "Teacher"}, {id: "3", name: "Jeff", age: "23", gender:"M", Role: "Student"}, {id: "4", name: "Ronald", age: "24", gender:"M", Role: "Teacher"}, {id: "5", name: "Ronak", age: "23", gender:"M", Role: "Student"}]
console .log (multipleMatch (['age', 'gender', 'Role']) (data2))
The makeKey and getProp functions could easily be inlined. But breaking out groupBy seems to make the problems much clearer.
This returns an array of arrays of objects. If you want just their names, it's easy to tack on .map (group => group .map (record => record .name)), or, replacing that initial .map with .flatMap, you get a single array at the cost of losing the potential for reporting on multiple groups.

Related

Map two separate arrays by matching object keys to values

Been scratching my head on this one for an entire evening with no solution in sight.
Put simply
I am querying two arrays from two separate APIs.
They return data in following format:
API 1
[{
balance: 4444,
age: "18",
gender: "Male",
level: "2",
name: "Joe"
}, {
balance: 3333,
age: "45",
gender: "Male",
level: "3",
name: "Angel"
}
}]
API 2
{
Joe: {
score: 32
},
Angel: {
score: 22
}
}
I need to match the object keys from the second API to the name value of playerInfo from first API so a new array is made that is completely flat like this:
[{
balance: 4444,
age: "18",
gender: "Male",
level: "2",
name: "Joe",
score: 32
}, {
balance: 3333,
age: "45",
gender: "Male",
level: "3",
name: "Angel",
score: 22
}
}]
Here's where I am being stone walled at the moment
var result = []
const matchKeys = (data, data1) => {
let arr = []
arr.push(data1)
data.map(item => {
arr.map(item1 => {
if (item.name === Object.keys(item1)) {
result.push(Object.assign(item, item1))
console.log(result)
}
})
})
}
matchKeys(api1, api2)
I suspect I'm not getting very far because I am not properly accessing my second dataset because there is no index that keeps track of which object I am supposed to pair up with corresponding value in the arrays.
Appreciate any help
You can implement that using Array.map.
const input1 = [{
balance: 4444,
age: "18",
gender: "Male",
level: "2",
name: "Joe"
}, {
balance: 3333,
age: "45",
gender: "Male",
level: "3",
name: "Angel"
}];
const input2 = {
Joe: {
score: 32
},
Angel: {
score: 22
}
}
function matchKeys(arr1, arr2) {
const result = arr1.map((item) => {
if (input2[item.name]) {
return { ...item, ...input2[item.name] };
}
return item;
});
return result;
}
console.log(matchKeys(input1, input2));
you could use the property of the second object as a way to search the right name.
const input1 = [{
balance: 4444,
age: "18",
gender: "Male",
level: "2",
name: "Joe"
}, {
balance: 3333,
age: "45",
gender: "Male",
level: "3",
name: "Angel"
}];
const input2 = {
Joe: {
score: 32
},
Angel: {
score: 22
}
}
const matchKeys = (data, data1) => {
return data.map((item) => ({ ...item, score: data1[item.name] ? data1[item.name].score : 0 }));
}
console.log(matchKeys(input1, input2));
also checked if it has a name and if for some reason it didn't I inserted a default score.

How to filter an array of objects with only certain properties? [duplicate]

This question already has answers here:
Extract certain properties from all objects in array
(5 answers)
Closed 2 years ago.
let arr = [
{ name: "rick", age: "44", phone: "33434", address: "florida" },
{ name: "sam", age: "33", phone: "23232", address: "milan" }
]
I only want to show only name and age, like this:
let filteredArr = [
{ name: "rick", age: "44"},
{ name: "sam", age: "33"}
]
With map
let arr = [
{ name: "rick", age: "44", phone: "33434", address: "florida" },
{ name: "sam", age: "33", phone: "23232", address: "milan" }
]
const newarr = arr.map(({name, age}) => ({name, age}))
console.log(newarr)
Based on https://stackoverflow.com/a/47916931/476951, you can do this dynamically
const arr = [
{ name: "rick", age: "44", phone: "33434", address: "florida" },
{ name: "sam", age: "33", phone: "23232", address: "milan" }
]
function filterMap (arr, fields) {
return arr.map(el =>
fields.reduce((a,c) => ({...a, [c]: el[c]}), {}))
}
const newarr = filterMap(arr, ['name', 'age'])
console.log(newarr)
In this case you map opt to use map instead of filter
let arr = [{
name: "rick",
age: "44",
phone: "33434",
address: "florida"
},
{
name: "sam",
age: "33",
phone: "23232",
address: "milan"
}
]
let filteredData = arr.map(item => {
return {
name: item.name,
age: item.age
}
});
console.log(filteredData)

How to merge the property with same key in two object array?

There are two object array, some of them have the same key, I'd like to merge the same key in the first array. I have pasted my code.I used nested loop, but the performance was bad O(n²). Maybe I need another method to enhance performance.(I can't use ES6 for some reason, so I'll appreciate if it is the ES5 method.)
var people = [
{
id: "001",
name: "David",
age: 29
},
{
id: "002",
name: "Lucia",
age: 41
},
{
id: "003",
name: "Steve",
age: 18
}
];
var address = [
{
id: "001",
city: "Barcelona"
},
{
id: "002",
city: "Paris"
},
{
},
{
id: "003",
city: "Tokyo"
},
{
id: "004",
city: "Barcelona"
}
];
My code
people.forEach(function(item) {
var id = item.id;
address.forEach(function(location) {
if (location.id == id) {
item.address = location.address
}
});
});
Result
var people = [
{
id: "001",
name: "David",
age: 29,
city: "Barcelona"
},
{
id: "002",
name: "Lucia",
age: 41,
city: "Paris"
},
{
id: "003",
name: "Steve",
age: 18,
city: "Tokyo"
}
];
The new people array is I preferred.
You could take a Map with all addresses and then map new object with extended properties of the map.
This approach takes all properties of address objects.
var people = [{ id: "001", name: "David", age: 29 }, { id: "002", name: "Lucia", age: 41 }, { id: "003", name: "Steve", age: 18 }],
address = [{ id: "001", city: "Barcelona" }, { id: "002", city: "Paris" }, {}, { id: "003", city: "Tokyo" }, { id: "004", city: "Barcelona" }],
map = new Map(address.map(o => [o.id, o])),
result = people.map(o => Object.assign({}, o, map.get(o.id)));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Make a Map with cities by id, and use it when iterating over the people array to find out the city:
let cities = new Map(address.map(a => [a.id, a.city]));
let people2 = people.map(p => ( {...p, city: cities.get(p.id)} ));
You could use Array#map to iterate over people, and Array#find to find the corresponding address from id within iterations:
const people = [{id: "001",name: "David",age: 29 },{ id: "002", name: "Lucia", age: 41
},{ id: "003", name: "Steve", age: 18 }],
address = [{ id: "001", city: "Barcelona" },{ id: "002", city: "Paris" },{ },{ id: "003", city: "Tokyo" },{ id: "004", city: "Barcelona" }];
console.log(
people.map(p => ({
...p,
...address.find(a => (p.id === a.id))
}))
);
However, that's supposing that the properties' name of address's items are not the same as people's ones.
The code below is not tested but it should work
// create an object to store them
const mergedItems = {};
// merge the 2 arrays so you only map them once (just for shorter code)
people.concat(address).map(entity => {
// add each entity on the object and id as a key
mergedItems[entity.id] = {
// if the key exist, it will merge it with the new entity
...mergedItems[entity.id],
...entity,
}
)
// this is your merged items
// Object.values will convert it from object to array
const finalItems = Object.values(mergedItems);
I used map instead of for loop because it is faster: https://codeburst.io/javascript-map-vs-foreach-f38111822c0f
I have used Object.assign method to add values from address
var people = [{ id: "001", name: "David", age: 29 }, { id: "002", name: "Lucia", age: 41 }, { id: "003", name: "Steve", age: 18 }],
address = [{ id: "001", city: "Barcelona" }, { id: "002", city: "Paris" }, {}, { id: "003", city: "Tokyo" }, { id: "004", city: "Barcelona" }];
people.forEach(function(item,pos){
Object.assign(item,{},address[address.findIndex(o=>o.id == item.id)]);
});
console.log(people);

How to use mapKeys and group values by key in JavaScript?

Initially, I'm having the object:
let root = {};
root["myRootNode"] = {
id1: {
age: "17",
name: "name1",
surname: "surname1"
},
id2: {
age: "11",
name: "name2",
surname: "surname2"
},
id3: {
age: "25",
name: "name1",
surname: "surname3"
}
};
And what I want to do with it, is to get it into the state:
"name1": [
{
age: "17",
surname: "surname1"
},
{
age: "25",
surname: "surname3"
}
],
"name2": [
age: "11",
surname: "surname2"
]
For me it's important to have the list/array of all objects, which contains this same property, grouped by the value of that property.
What I tried (using lodash) is:
let test = _.mapKeys(root["myRootNode"], function(value, key) {
  return value["name"];
});
But this gives me the result:
"name1": {
age: "25"
name: "name1"
surname: "surname3"
},
"name2": {
age: "11"
name: "name2"
surname: "surname2"
}
So they are not grouped and only the last value is mapped under the key which is repeating. Also in the result that I got, they are not placed under an array.
Can use groupBy and map it's values to get rid of the name property.
If you don't mind leaving the name property can simply do _.groupBy(root.myRootNode, 'name');
Personally it feels like you should be using arrays instead of objects
const res =
_(root.myRootNode)
.groupBy('name')
.mapValues((arr)=>_.map(arr, (o) =>_.omit(o,['name'])))
.value()
console.log(res)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
<script>
let root = {};
root["myRootNode"] = {
id1: {
age: "17",
name: "name1",
surname: "surname1"
},
id2: {
age: "11",
name: "name2",
surname: "surname2"
},
id3: {
age: "25",
name: "name1",
surname: "surname3"
}
};
</script>
Here's a shot at it. The idea is to create an array for each new name and push all entries onto the corresponding name array.
const root = {
myRootNode: {
id1: {
age: "17",
name: "name1",
surname: "surname1"
},
id2: {
age: "11",
name: "name2",
surname: "surname2"
},
id3: {
age: "25",
name: "name1",
surname: "surname3"
}
}
};
const result = Object.values(root.myRootNode).reduce((a, e) => {
if (!(e.name in a)) {
a[e.name] = [];
}
a[e.name].push({
age: e.age,
surname: e.surname
});
return a;
}, {});
console.log(JSON.stringify(result, null, 4));

How to get the index of an object in an array of objects when it matches completely javascript

I have an array of objects and I would like to get the index of the object in the array when I get a match.
I have the array as follows:
let x = [
{name: "emily", info: { id: 123, gender: "female", age: 25}},
{name: "maggie", info: { id: 234, gender: "female", age: 22}},
{name: "kristy", info: { id: 564, gender: "female", age: 26}},
.....
];
Currently I am using indexOf which worked initially and now it doesn't work properly. It returns -1.
let find = {name: "maggie", info: { id: 234, gender: "female", age: 22}};
let index = x.indexOf(find); // should return 1.
The whole should match in the array and should return the index of that object. How can I achieve this? Should I be using some() ?
Thank you
You can use .find instead of indexOf as 2 objects are never equal ( as they point to different reference in memory ) which is what you seem to pass as an argument.
let x = [
{name: "emily", info: { id: 123, gender: "female", age: 25}},
{name: "maggie", info: { id: 234, gender: "female", age: 22}},
{name: "kristy", info: { id: 564, gender: "female", age: 26}}
];
let found = x.find(function(item) {
// you can use the condition here
return item.info.id === 564;
});
console.log(found);
To find the index, you can use .findIndex method instead.
let x = [
{name: "emily", info: { id: 123, gender: "female", age: 25}},
{name: "maggie", info: { id: 234, gender: "female", age: 22}},
{name: "kristy", info: { id: 564, gender: "female", age: 26}}
];
let foundIndex = x.findIndex(function(item) {
// you can use the condition here
return item.info.id === 564;
});
console.log(foundIndex);
Objects cannot be compared by traditional equality in JavaScript. Instead, use the ES6 findIndex method to compare each object's properties with the desired values. Here is an example:
let x = [
{name: "emily", info: { id: 123, gender: "female", age: 25}},
{name: "maggie", info: { id: 234, gender: "female", age: 22}},
{name: "kristy", info: { id: 564, gender: "female", age: 26}}
];
let find = {name: "maggie", info: { id: 234, gender: "female", age: 22}};
let index = x.findIndex(element => element.info.id === find.info.id); // index === 1
The id value seems to be sufficient to identify an object in your scenario; if you need to compare more properties, you could obviously add additional equality checks (e.g., element.name === find.name) with the && operator.
If we live in the _.lodash world than this works since lodash would go deep on objects:
let data = [
{name: "emily", info: { id: 123, gender: "female", age: 25}},
{name: "maggie", info: { id: 234, age: 22, gender: "female"}},
{name: "kristy", info: { id: 564, gender: "female", age: 26}},
];
let find = {name: "maggie", info: { id: 234, gender: "female", age: 22}};
let index = _.findIndex(data, (i) => _.isEqual(i, find))
console.log(index) // 1
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
A more brutish approach which obviously it is not performant
and as pointed out wont work if the order of the props is different.
let data = [
{name: "emily", info: { id: 123, gender: "female", age: 25}},
{name: "maggie", info: { id: 234, gender: "female", age: 22}},
{name: "kristy", info: { id: 564, gender: "female", age: 26}},
];
var objectJSONs = data.map((i) => JSON.stringify(i))
let myJSON = JSON.stringify({name: "maggie", info: { id: 234, gender: "female", age: 22}});
let index = objectJSONs.indexOf(myJSON)
console.log(index) // 1
You can make use of underscore _.isEqual for Object comparison and some() or any looping mechanism to iterate the array.
let iFoundIndex = -1;
let bFound = x.some((data,index) => {
if(_.isEqual(data,find){
iFoundIndex = index;
return true;
}
return false;
}
//console.log(iFoundIndex);

Categories

Resources