Using Array.reduce() to group by [duplicate] - javascript

This question already has answers here:
Most efficient method to groupby on an array of objects
(58 answers)
Closed 4 years ago.
I have an array like this (1st level nesting is intended)
const names = [
[{
name: "John",
country: "USA"
}, {
name: "Mary",
country: "UK"
}], {
name: "Paul",
country: "USA"
}
]
I would like to convert this into
const groupBy = [{
country: "USA",
names: [{
name: "John"
}, {
name: "Paul"
}]
},
{
country: "UK",
names: [{
name: "Mary"
}]
}
]
I am wondering how I could use reduce() or another method to achieve the desired outcomes?

You could use lodash flatten to remove nesting, but I handle the nesting by calling reduce again. Lodash also has a groupBy but that gives you a keyed object, which is not what you want. So here is a non-lib based solution, it supports infinite nesting of arrays.
const names = [[{
name: "John",
country: "USA"
}, {
name: "Mary",
country: "UK"
}], {
name: "Paul",
country: "USA"
}];
const reducer = (groupBy, el) => {
if (Array.isArray(el)) {
return el.reduce(reducer, groupBy);
} else {
const { country, ...rest } = el;
const group = groupBy.find(el => el.country === country);
if (group) {
group.names.push(rest);
} else {
groupBy.push({
country,
names: [rest]
})
}
return groupBy;
}
}
const groupBy = names.reduce(reducer, []);
console.log('groupBy:', groupBy);

Related

Remove duplicates from an array of objects based on time created

I have an array of objects and there are some duplicate objects
const data = [
{
"id": "1011",
"name": "abc",
"Dob": "3/2/11",
"timeCreated": "16:03:41"
},
{
"id": "1012",
"name": "xys",
"Dob": "6/5/12",
"timeCreated": "01:05:21"
},
{
"id": "1011",
"name": "xyz",
"Dob": "3/2/11",
"timeCreated": "17:03:41"
},
{
"id": "1011",
"name": "xyz",
"Dob": "3/2/11",
"timeCreated": "15:03:41"
}
]
I am removing duplicates in the array by using some()
let arr = [];
data.forEach(obj => {
if (!arr .some(o => o.id === obj.id)) {
arr.push({ ...obj})
}
});
I need help filtering it and only keeping the latest object based off of "timeCreated"
so the data looks something like this:
{
"id": "1012",
"name": "xys",
"Dob": "6/5/12",
"timeCreated": "01:05:21"
},
{
"id": "1011",
"name": "xyz",
"Dob": "3/2/11",
"timeCreated": "17:03:41"
},
]
you can do that :
const data =
[ { id: '1011', name: 'abc', Dob: '3/2/11', timeCreated: '16:03:41' }
, { id: '1012', name: 'xys', Dob: '6/5/12', timeCreated: '01:05:21' }
, { id: '1011', name: 'xyz', Dob: '3/2/11', timeCreated: '17:03:41' }
, { id: '1011', name: 'xyz', Dob: '3/2/11', timeCreated: '15:03:41' }
]
const arr = data.reduce((result,obj)=>
{
let row = result.find(x=>x.id===obj.id)
if (!row)
result.push({...obj})
else if (row.timeCreated < obj.timeCreated)
Object.assign(row,obj)
return result
},[])
console.log( arr )
.as-console-wrapper {max-height: 100%!important;top:0 }
Late to the party, but here's a shorter, maybe faster, more efficient solution that just involves a sort and filter operation
let tmp=[], arr = data.sort((a, b) => +b.timeCreated.replaceAll(':', '') - +a.timeCreated.replaceAll(':', ''))
.filter(o => (!tmp.includes(o.id) && tmp.push(o.id)));
How it works: Pretty simply actually. It first sorts the array by timeCreated descending. It does this by (on the fly) transforming the 'HH:MM:SS' string into the number HHMMSS (+b.timeCreated.replaceAll(':', '')), then comparing. Then it takes the sorted array and filters it through the temporary array tmp, which stores ids each iteration - and if the id is already in there (and we know that is the latest according to the timeCreated) we filter it out. This is all handled by the wonderfully simple ternary: .filter(o => (!tmp.includes(o.id) && tmp.push(o.id)), which says if we've already seen that id, return false, otherwise make a note of it
Why it's cool - For most use cases (small data sets), there isn't a significant difference between functional iterators like map, reduce, forEach, filter, sort - however this is thinking out of the box. Rather than build datasets and reduce them down, this smartly chops it to size first - using only 2 operations.
const data = [{ id: '1011', name: 'abc', Dob: '3/2/11', timeCreated: '16:03:41' }, { id: '1012', name: 'xys', Dob: '6/5/12', timeCreated: '01:05:21' }, { id: '1011', name: 'xyz', Dob: '3/2/11', timeCreated: '17:03:41' }, { id: '1011', name: 'xyz', Dob: '3/2/11', timeCreated: '15:03:41' }]
let tmp=[], arr = data.sort((a, b) => +b.timeCreated.replaceAll(':', '') - +a.timeCreated.replaceAll(':', '')).filter(o => (!tmp.includes(o.id) && tmp.push(o.id)));
console.log(arr)

How do I combine 2 array of objects having common key value pair in node js [duplicate]

This question already has answers here:
How to merge multiple array of object by ID in javascript?
(5 answers)
Closed 1 year ago.
Input arrays
arr1=[{"CATALOG":"book1","ID":"1"},{"CATALOG":"book2","ID":"2"},{"CATALOG":"book3","ID":"3"},{"CATALOG":"book4","ID":"12"}]
arr2=[{"NAME":"TOM","ID":"1"},{"NAME":"STEVE","ID":"22"},{"NAME":"HARRY","ID":"2"},{"NAME":"TIM","ID":"3"},{"NAME":"DAVE","ID":"12"},{"NAME":"WIL","ID":"12"},{"NAME":"PETER","ID":"94"},{"NAME":"SAVANNAH","ID":"77"}]
Expected Output
[{"CATALOG":"book1","ID":"1","NAME":"TOM"},
{"CATALOG":"book2","ID":"2","NAME":"HARRY"},
{"CATALOG":"book3","ID":"3","NAME":"TIM"},
{"CATALOG":"book4","ID":"12","NAME":"WIL"}
expected output is that 2 arrays have to be combined based on id. If ID doesn't exist then that particular object is skipped
I tried using
[arr1,arr2].reduce((a, b) => a.map((c, i) => Object.assign({}, c, b[i])));
But not getting the desired output
You can use map and find
const arr1 = [
{ CATALOG: "book1", ID: "1" },
{ CATALOG: "book2", ID: "2" },
{ CATALOG: "book3", ID: "3" },
{ CATALOG: "book4", ID: "12" },
];
const arr2 = [
{ NAME: "TOM", ID: "1" },
{ NAME: "STEVE", ID: "22" },
{ NAME: "HARRY", ID: "2" },
{ NAME: "TIM", ID: "3" },
{ NAME: "DAVE", ID: "12" },
{ NAME: "WIL", ID: "12" },
{ NAME: "PETER", ID: "94" },
{ NAME: "SAVANNAH", ID: "77" },
];
const output = arr1.map(a => ({
...a,
NAME: arr2.find(x => x.ID === a.ID).NAME,
}));
console.log(output);
There may be cleverer solutions, but assuming arr2 always contains the corresponding ID, I'd do it simply with :
const arr1=[{"CATALOG":"book1","ID":"1"},{"CATALOG":"book2","ID":"2"},{"CATALOG":"book3","ID":"3"},{"CATALOG":"book4","ID":"12"}];
const arr2=[{"NAME":"TOM","ID":"1"},{"NAME":"STEVE","ID":"22"},{"NAME":"HARRY","ID":"2"},{"NAME":"TIM","ID":"3"},{"NAME":"DAVE","ID":"12"},{"NAME":"WIL","ID":"12"},{"NAME":"PETER","ID":"94"},{"NAME":"SAVANNAH","ID":"77"}];
const output = arr1.map(obj1 => {
const obj2 = arr2.find(o2 => o2.ID === obj1.ID);
return Object.assign(obj1, obj2)
});
console.log(output)
Or as a one-liner :
const output = arr1.map(obj1 => Object.assign(obj1, arr2.find(o2 => o2.ID === obj1.ID)))

Trying to filter an array of objects that has a property value that is also an array

I have this object inside a json:
{
"group": "A",
"id": "50"
"person": [
{
"name": 'Joe',
"age": '29'
"hobbies": ["Watching movies", "Gaming"]
},
{
"name": 'Jessie',
"age": '27'
"hobbies": ["Gaming", "Reading"]
}
]
}
I want to filter the people by their hobbies. For example, if I filter by Gaming I need to create an array of objects with Joe and Jessie. If I filter by Reading, then the array would only have Jessie.
Here is my code:
import { people } from '../../../data/index' // this is the json I shower above
let filteredArray;
filteredArray = people.filter(person => {
return person.hobbies == "Gaming";
})
this doesn't work, but if change the hobbies to a single word on the json, like this:
{
"name": 'Jessie',
"age": '27'
"hobbies": "Gaming"
}
then it work just fine.
So is there a way to use filter with a array of hobbies to check if one of the values match my condition?
I'm using only vanilla Js and I only want to support chrome.
Sorry for any english mistake or if the question is not clear enought, I'm still in the beggining of my studies
You have to use includes method. Because you are trying to search in array:
const filteredArray = people.filter(person => {
return person.hobbies.includes("Gaming");
})
const data = {
group: 'A',
id: '50',
person: [
{
name: 'Joe',
age: '29',
hobbies: ['Watching movies', 'Gaming']
},
{
name: 'Jessie',
age: '27',
hobbies: ['Gaming', 'Reading']
}
]
};
const result = data.person.filter(el => el.hobbies.includes('Gaming'));
console.log(result);

Calling multiple nested object recursively giving undefined

I wrote a simple algo whose job is to find the corresponding name where the profession is teacher.
The given code calls the function recursively until the given result is achieved.
On executing the code, I am getting the final output is undefined. where as I was expecting the name to be ishan.
Can someone help me in diagnosing the problem in my algo?
//Accessing infitely nested Array
// Infinitely nested Array
const infiniteArray = [
{
name: "Jack",
age: "98",
profession: "doctor",
children: [
{
name: "Varun",
age: "80",
profession: "scientist",
children: [
{
name: "Ishan",
age: "62",
profession: "teacher"
}
]
}
]
}
];
const accessNestedObject = (infiniteArray) => {
return infiniteArray.forEach(element => {
if (element['profession'] === 'teacher') {
console.log(element.name)
return element.name
} else {
console.log(element["children"])
return accessNestedObject(element["children"])
}
});
}
const result = accessNestedObject(infiniteArray)
console.log(result)
You are getting undefined because that's the expected return value of Array#forEach.
You have to declare a variable that will store the final result of your loop.
//Accessing infitely nested Array
// Infinitely nested Array
const infiniteArray = [
{
name: "Jack",
age: "98",
profession: "doctor",
children: [
{
name: "Varun",
age: "80",
profession: "scientist",
children: [
{
name: "Ishan",
age: "62",
profession: "teacher"
}
]
}
]
}
];
const accessNestedObject = (infiniteArray) => {
let result = null;
infiniteArray.forEach(element => {
if (element.profession === 'teacher') {
result = element.name;
} else {
result = accessNestedObject(element.children);
}
});
return result;
}
const result = accessNestedObject(infiniteArray);
console.log(result);

Set a property of each object of an array by default in react js [duplicate]

This question already has answers here:
How to set specific property value of all objects in a javascript object array (lodash)
(3 answers)
Closed 4 years ago.
I am declaring an empty array of objects called filter in constructor:
constructor(props){
super(props);
this.state = {
filter: []
}
}
I am getting an API response which is an array objects something like below and storing in filter variable.
[
{
name: "xyz",
language: "english",
age: 20
},
{
name: "abc",
language: "history",
age: 21
},
{
name: "efi",
language: "geography",
age: 20
}
]
I was looking for a way where I can set a property like checked = false for each object in filter array by default in jsx file. Though the response from API is above, after receiving the response, the array should like
[
{
name: "xyz",
language: "english",
age: 20,
**checked: false**
},
{
name: "abc",
language: "history",
age: 21,
**checked: false**
},
{
name: "efi",
language: "geography",
age: 20,
**checked: false**
}
]
using map and Object.assign, assuming obj is the respanse that you get from your api you can run the function below to parse your array an then set the respanse to your state
var obj=[
{
name: "xyz",
language: "english",
age: 20
},
{
name: "abc",
language: "history",
age: 21
},
{
name: "efi",
language: "geography",
age: 20
}
]
var result = obj.map(el => {
var o = Object.assign({}, el);
o.checked = false;
return o;
})
console.log(result);
Api.post(JSON.stringify(ConditionJson), AppConst.BaseURL + 'query/execute/conditions/LoyaltyStamps', (err, responseJson) => {
if (err) {
Alert.alert('Error!', err);
}
else {
var checked = 'false';
var name = responseJson.map(function (info) {
checked += 'false';
});
this.setState({
ischecked: checked
});
}

Categories