Find objects that have duplicate names inside the array in JavaScript - javascript

I have an array that I find in the searchName section, and in the resultName section I separate the duplicate names, and in the filters section I want to display the objects that have those duplicate names in the console.log, but an empty array
Please help me to get the answer
const data = [
{id: 1,name: "Liam",age: 20},
{id: 1,name: "Noah",age: 22},
{id: 1,name: "Liam",age: 20},
{id: 1,name: "Elijah",age: 18},
{id: 1,name: "Elijah",age: 18}
]
const searchName = data.map(item => item.name)
console.log(searchName);
const toFindDuplicates = arry => arry.filter((item, index) => arry.indexOf(item) !== index);
const resultName = toFindDuplicates(searchName)
console.log(resultName);
const filters = data.filter(x => x.Name === resultName)
console.log(filters);

Use .includes() to check if the name of an element exists inside the resultName array. You also had a typo: x.name instead of x.Name
const data = [{
id: 1,
name: "Liam",
age: 20
},
{
id: 1,
name: "Noah",
age: 22
},
{
id: 1,
name: "Liam",
age: 20
},
{
id: 1,
name: "Elijah",
age: 18
},
{
id: 1,
name: "Elijah",
age: 18
}
]
const searchName = data.map(item => item.name)
console.log(searchName);
const toFindDuplicates = arry => arry.filter((item, index) => arry.indexOf(item) !== index);
const resultName = toFindDuplicates(searchName)
console.log(resultName);
const filters = data.filter(x => resultName.includes(x.name))
console.log(filters);

You need to fix your filter function.
In your version you trying to compare an array to an string.
What you need to do is something like this:
const filters = data.filter(x => resultName.includes(x.name))

First you can count the names and then fill the array
const data = [
{id: 1,name: "Liam",age: 20},
{id: 1,name: "Noah",age: 22},
{id: 1,name: "Liam",age: 20},
{id: 1,name: "Elijah",age: 18},
{id: 1,name: "Elijah",age: 18}
]
const obj = data.reduce((acc, el) => { acc[el.name] = (acc[el.name] ?? 0) + 1; return acc; }, {});
const all = Object.keys(obj);
const duplicates = Object.entries(obj).filter(el => el[1] > 1).map(el => el[0]);
const uniques = Object.entries(obj).filter(el => el[1] === 1).map(el => el[0]);
console.log(all);
console.log(duplicates);
console.log(uniques);

Related

How to group array of object by key value pairs using javaScript?

I just started learning JavaScript, I have this type of array, how I can turn this array of objects into key-value pairs like below, Any source and reference is acceptable.
Sample Array:
[
{Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1},
{Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1}
]
Expected Result:
{
"6d7e75e6-c58b-11e7-95-ac162d77eceb":1,
"6d2e75e6-c58b-11e7-95-ac162d77eceb":1
}
Using Array.prototype.Reduce:
const arr = [{Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1},{Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1}];
const result = arr.reduce((acc, { Id, qty }) => ({ ...acc, [Id]: qty }), {});
console.log(result);
Another approach, a little more beginner friendly.
const arr = [
{Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1},
{Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1}
];
const newObject = {}; // empty object
// loop over each element of the array
arr.forEach(element => {
// the key is the element identifier (Id) and the value is the element quantity (qty)
newObject[element.Id] = element.qty;
});
You can use a loop and add the item.Id as the key and the item.qty as the value in an empty object.
let arr = [{Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1},{Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1}]
let obj = {}
arr.forEach(item => {
obj[item.Id] = item.qty
})
console.log(obj)
You can easily achieve this result using forEach in a single line of code.
const arr = [
{ Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 },
{ Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 },
];
const result = {};
arr.forEach(({ Id, qty }) => (result[Id] = qty));
console.log(result);
You can achieve the desired result with below code
//input array
const arrList = [
{Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1},
{Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1}
]
function findArray(arr) {
//define a new array to store Id's
let newArray = [];
//iterate through array items
arr.forEach(item => {
newArray.push(item.Id);
});
return newArray;
}
//call findArray function to get desired output
console.log(findArray(arrList));
Using Object.fromEntries()
const
array = [{ Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 }, { Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 }],
object = Object.fromEntries(array.map(({ Id, qty }) => [Id, qty]));
console.log(object);
or, for some fragile novelty...
const
array = [{ Id: "6d7e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 }, { Id: "6d2e75e6-c58b-11e7-95-ac162d77eceb", qty: 1 }],
object = Object.fromEntries(array.map(Object.values));
console.log(object);

Filtering two arrays with .filter [duplicate]

This question already has answers here:
Simplest code for array intersection in javascript
(40 answers)
Closed 2 years ago.
I'm trying to get information, a have all the data that i get from Database and now I need to do something that should be simple (but not for a novice like me xd), I'm trying to do the next filter in JS
const array1 = [{id: 'a'}, {id: '8'}, {id: 'c'}, {id: 'a'}];
const array2 = [{id: 'a'}, {id: 'c'}];
console.log(array1.filter(id => id.id == array2.id))
It doesn't returns me nothing, and I don't understand why, also I try to use:
console.log(array1.filter(id => id.id == array2.id))
My result is all the values from array1, I mean I only want the elements that have the same Id that array2 from array1, in other words, I want that its returns me 2 objects with Id = a and one with id = c
Use Array.some() inside Array.filter() callback method.
const array1 = [{id: 'a'}, {id: '8'}, {id: 'c'}, {id: 'a'}];
const array2 = [{id: 'a'}, {id: 'c'}];
const output = array1.filter(item1 => array2.some(item2 => item2.id === item1.id))
console.log(output);
This will return three objects, because in array1 there are two objects with id: a and one with id: c.
const array1 = [{id: 'a'}, {id: '8'}, {id: 'c'}, {id: 'a'}];
const array2 = [{id: 'a'}, {id: 'c'}];
let res = array1.filter(obj1 => array2.find(obj2 => obj1.id === obj2.id))
console.log(res)
You can filter out duplicate objects with Array.reduce():
const array1 = [{id: 'a'}, {id: '8'}, {id: 'c'}, {id: 'a'}];
const array2 = [{id: 'a'}, {id: 'c'}];
let res = array1.filter(obj1 => {
return array2.find(obj2 => obj1.id === obj2.id)
})
.reduce((acc,cur) => {
if(!acc.find(obj => obj.id === cur.id)){
acc.push(cur)
}
return acc
},[])
console.log(res)
You could take a Set and filter the array.
This approach takes a single loop for building the set and another for filtering.
const
array1 = [{ id: 'a' }, { id: '8' }, { id: 'c' }, { id: 'a' }],
array2 = [{ id: 'a' }, { id: 'c' }],
set2 = new Set(array2.map(({ id }) => id)),
result = array1.filter(({ id }) => set2.has(id));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can to use .some to check if array2 has an element with a given id:
const array1 = [{id: 'a'}, {id: '8'}, {id: 'c'}, {id: 'a'}];
const array2 = [{id: 'a'}, {id: 'c'}];
const res = array1.filter(({id}) => array2.some(e => e.id===id));
console.log(res);
A better way would be using a Set:
const array1 = [{id: 'a'}, {id: '8'}, {id: 'c'}, {id: 'a'}];
const array2 = [{id: 'a'}, {id: 'c'}];
const idsInArray2 = new Set(array2.map(({id}) => id));
const res = array1.filter(({id}) => idsInArray2.has(id));
console.log(res);

Filtering using two array of objects using JavaScript

I have an array as shown:
var arrOne = [{id: 3},{id: 8},{id: 12}];
And another array as shown:
var arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
I want to iterate arrTwo based on arrOne, and get the val values out of arrTwo.
So the result should be:
var result = ['Bailey', 'cathy', 'David'];
Tried concatenating .map with .filter:
arrOne.map(arOne => arrTwo.filter(artwo => {
if(arOne.id === artwo.id) {
return artwo.val
} else {
return false;
}
}));
But it gives me all, and where it is false it adds false there, which I don't want.
Any ideas where I am going wrong will be appreciated.
Editing as per norbitrial's answer:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
const result = arrOne.map(({id}) => arrTwo.find(e => {
const someCond = someConditionaEval();
if(someCond && e.id === id) {
return e;
} else {
return false;
}
}).val); //this breaks
Using .map() and .find() combination:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const result = arrOne.map(({id}) => arrTwo.find(e => e.id === id).val);
console.log(result);
I hope this helps!
You can use .filter() method on arrTwo and then using .includes() method get the filtered objects from arrTwo and then finally using .map() get only the val property values from each filtered object like:
var arrOne = [{id: 3},{id: 8},{id: 12}];
var arrTwo = [{id:1,val:"Adam"},{id:3,val:"Bailey"},{id:8,val:"Cathy"},{id:12,val:"David"},{id:15,val:"Earl"}];
var result = arrTwo.filter(a => arrOne.map(o=>o.id).includes(a.id)).map(o=>o.val)
console.log( result )
You could take an object with the values and then map the wanted values.
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }],
arrTwo = [{ id: 1, val: 'Adam' }, { id: 3, val: 'Bailey' }, { id: 8, val: 'Cathy' }, { id: 12, val: 'David' }, { id: 15, val: 'Earl' }],
values = arrTwo.reduce((r, { id, val }) => (r[id] = val, r), {}),
result = arrOne.map(({ id }) => values[id]);
console.log(result);
Create a Map of val by id from arrTwo, and then map arrOne, and extract the val from the Map using the id.
Why I prefer creating a Map/dictionary (object) instead of using Array.map() with Array.find()?
Because of the complexity - Array.map() with Array.find(), for example, is O(n * m), while creating a Map and then using Array.map() to get the values is O(n + m). However, if you've got two small arrays, this shouldn't actually hurt actual performance.
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const valById = new Map(arrTwo.map(({ id, val }) => [id, val]));
const result = arrOne.map(o => valById.get(o.id));
console.log(result);
Build an object from arrTwo to gather val's in one iteration.
use map on arrOne and get val from above object.
const update = (arr1, arr2) => {
const all = Object.fromEntries(arr2.map(({ id, val }) => [id, val]));
return arr1.map(({ id }) => all[id]);
};
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }];
var arrTwo = [
{ id: 1, val: "Adam" },
{ id: 3, val: "Bailey" },
{ id: 8, val: "Cathy" },
{ id: 12, val: "David" },
{ id: 15, val: "Earl" }
];
console.log(update(arrOne, arrTwo));

Convert array of objects in an array of array of objects [duplicate]

This question already has answers here:
How can I group an array of objects by key?
(32 answers)
Closed 2 years ago.
I am retrieving data from a football (soccer) API. The specific data I need is an array of objects (306 objects). Every object has a property called matchday with a numeric value. I want to group all the objects that share the same property and store them in an array. What I need in the end is an array of array of objects.
Example array of objects:
[
{id: 264796, matchday: 1, …},
{id: 264797, matchday: 1, …},
{id: 264798, matchday: 2, …},
{id: 264800, matchday: 2, …},
]
What I want looks like this:
[
[{id: 264796, matchday: 1, …},{id: 264797, matchday: 1, …}],
[{id: 264798, matchday: 2, …},{id: 264800, matchday: 2, …}],
]
You can use .reduce() with Object.values() to get the desired output:
const data = [
{id: 264796, matchday: 1}, {id: 264797, matchday: 1},
{id: 264798, matchday: 2}, {id: 264800, matchday: 2}
];
const result = Object.values(
data.reduce((r, c) => {
r[c.matchday] = r[c.matchday] || [];
r[c.matchday].push(c);
return r;
}, {})
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
we can use reduce
const arr = [
{id: 264796, matchday: 1},
{id: 264797, matchday: 1},
{id: 264798, matchday: 2},
{id: 264800, matchday: 2},
]
const result = arr.reduce((acc, item) => {
if (!acc.find(accSubArr => accSubArr.find(accSubArrItem => accSubArrItem.matchday === item.matchday))) {
acc.push(arr.filter(arrItem => arrItem.matchday === item.matchday))
}
return acc;
}, [])
console.log(result)
You can try this:
const data = [{
id: 264796,
matchday: 1
},
{
id: 264797,
matchday: 1
},
{
id: 264798,
matchday: 2
},
{
id: 264800,
matchday: 2
},
]
const group = data
.map(d => d.matchday)
.filter((v, i, c) => c.indexOf(v) === i)
.map(i => data.filter(d => d.matchday === i))
console.log(group)
To add to the existing answers, here's another way making use of Map with a predicate to determine the group-by value:
const groupBy = predicate => items =>
Array.from(items.reduce((agg, next) => {
const key = predicate(next);
return agg.set(key, [].concat(agg.get(key) || []).concat(next));
}, new Map()).values());
const data = [
{name: 'A', key: 1},
{name: 'B', key: 1},
{name: 'C', key: 2},
{name: 'D', key: 2},
{name: 'E', key: 3}
];
const grouped = groupBy(x => x.key)(data);
For the fun of it, here's a recursive version of groupBy:
const groupBy = predicate => function group([next, ...items], grouped = new Map()) {
if (!next) {
return Array.from(grouped.values())
}
const key = predicate(next);
return group(items, grouped.set(key, [...(grouped.get(key) || []), next]));
}
And what the heck, here's a more imperative approach to add to the mix:
const groupBy = predicate => items => {
const cache = {};
for(item of items) {
const key = predicate(item);
cache[key] = [].concat(cache[key]).concat(item).filter(x => x)
}
return Object.values(cache);
}

Get count from Array of arrays

I have an array of arrays below. With ES6, how can I get a count of each value Good, Excellent & Wow into a new array e.g [{name: Good, count: 4} {name: Excellent, count: 5}, {name:Wow, count:2}] in dynamic style. I am attempting to use Object.assign but I am failing to "unique" out the count of the key plus instead, I need to use an array as I am trying to render this out on the front end. Do I need to use reduce? how?
let k = 0
const stats = {}
const remarks = [
[{name: "Good"}],
[{name: "Good"}, {name: "Excellent"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Excellent"}],
[{name: "Excellent"}]
]
remarks.forEach((arr) => {
arr.map((e) => {
Object.assign(stats, { [e.name]: k = k + 1 })
})
})
console.log(stats);
Output:
stats: {Good: 8, Excellent: 11, Wow: 9}
Which is Incorrect plus I need to use an array.
Expected output:
[{name: Good, count: 4} {name: Excellent, count: 5}, {name:Wow, count:2}]
Flatten the array of arrays and reduce it starting with an object like : { Good: 0, Excellent: 0, Wow: 0}
then .map the Object.entries of the result to transform it to an array :
const remarks = [
[{ name: "Good" }],
[{ name: "Good" }, { name: "Excellent" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Excellent" }],
[{ name: "Excellent" }]
];
const result = Object.entries(
remarks.flat().reduce(
(all, { name }) => {
all[name] += 1;
return all;
},
{ Good: 0, Excellent: 0, Wow: 0 }
)
).map(([name, count]) => ({ name, count }));
console.log(result);
You can try below logic:
var data = [[{name: "Good"}],[{name: "Good"}, {name:"Excellent"}],[{name: "Good"}, {name:"Excellent"}, {name:"Wow"}],[{name: "Good"}, {name:"Excellent"}, {name:"Wow"}],[{name:"Excellent"}],[{name:"Excellent"}]]
var nData = [];
(data || []).forEach( e => {
(e || []).forEach(ei => {
var i = (index = nData.findIndex(d => d.name === ei.name)) >=0 ? index : nData.length;
nData[i] = {
name: ei.name,
count : (nData[i] && nData[i].count ? nData[i].count : 0)+1
}
});
});
console.log(nData);
Hope this helps!
You can use reduce, then convert the result into an array of objects:
const counts = remarks.reduce((result, list) => {
list.forEach(remark => {
result[remark.name] = (result[remark.name] || 0) + 1;
});
}, {});
const finalResult = [];
for (let name in counts) {
finalResult.push({name, count: counts[name]});
}
You could achieve this pretty easily by:
1) Flattening the nested array into 1 single level array.
2) Iterating over the flat array and create a "count map" by using Array.prototype.reduce
For example:
const remarks = [
[{
name: 'Good'
}],
[{
name: 'Good'
}, {
name: 'Excellent'
}],
[{
name: 'Good'
}, {
name: 'Excellent'
}, {
name: 'Wow'
}],
[{
name: 'Good'
}, {
name: 'Excellent'
}, {
name: 'Wow'
}],
[{
name: 'Excellent'
}],
[{
name: 'Excellent'
}]
]
const flatten = arr => arr.reduce((accum, el) => accum.concat(el), [])
const map = flatten(remarks).reduce((accum, el) => {
if (accum[el.name]) {
accum[el.name] += 1;
} else {
accum[el.name] = 1;
}
return accum;
}, {});
console.log(map)
First find the counts using reduce than pass that to another function to get the desired view structure:
const Good = 1,
Excellent = 2,
Wow = 3;
const remarks = [
[{name: Good}],
[{name: Good}, {name:Excellent}],
[{name: Good}, {name:Excellent}, {name:Wow}],
[{name: Good}, {name:Excellent}, {name:Wow}],
[{name:Excellent}],
[{name:Excellent}]
];
/*
[{name: Good, count: 4} {name: Excellent, count: 5}, {name:Wow, count:2}]
*/
function counts(remarks) {
return remarks.flat().reduce((acc, v) => {
const name = v.name;
let count = acc[name] || 0;
return {
...acc,
[name]: count + 1
}
}, {});
}
function view(counts) {
return Object.keys(counts).map(key => {
let count = counts[key];
return { name: key, count };
})
}
console.log(view(counts(remarks)));
Any time you are making a smaller set of data, or transforming data, in JavaScript reduce should be the first method you attempt to use. In this case, you may want to pair it with an indexer (hence preloading with an array of index and an array of result).
This works in one pass without needing to know the name values up front.
const remarks = [
[{name: "Good"}],
[{name: "Good"}, {name: "Excellent"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Excellent"}],
[{name: "Excellent"}]
];
const stats = remarks.reduce((p,c) => (
c.forEach( ({name}) => {
if(!p[0].hasOwnProperty(name)){
p[1].push({name:name,count:0});
p[0][name] = p[1].length - 1;
}
p[1][p[0][name]].count++;
}),p),[{},[]])[1];
console.log(stats);
A slightly more concise and definitely less readable approach (but it's worth to mention) could be:
const remarks = [
[{ name: "Good" }],
[{ name: "Good" }, { name: "Excellent" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Excellent" }],
[{ name: "Excellent" }]
];
const stats = Object.entries(
remarks
.flat()
.reduce((acc, {name}) => (acc[name] = -~acc[name], acc), {})))
).map(([name, count]) => ({ name, count }));
console.log(stats);
It uses the comma operator in the reducer to returns the accumulator; and the bitwise operator NOT to create a counter without the needs to initialize the object upfront with all the names.
const flattenedRemarks = _.flatten(remarks);
const groupedRemarks = _.groupBy(flattenedRemarks, (remark) => remark.name);
const remarkCounts = _.mapValues(groupedRemarks, (group) => group.length);
const data = {
"mchale": {
"classes":["ESJ030", "SCI339"], // get the length
"faculty":["Hardy", "Vikrum"] // get the length
},
"lawerence":{
"classes":["ENG001"], // get the length
"faculty":["Speedman", "Lee", "Lazenhower"] // get the length
}
};
const count = Object.keys(data).map(campusName => {
const campus = data[campusName];
return Object.keys(campus).map(key => campus[key].length).reduce((p, c) => p + c, 0);
}).reduce((p, c) => p + c, 0);
console.log(count);

Categories