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)
Related
I need to change the keys of my object. I could use the map function to change the keys of my outer object. Question is, how can I access the inner object which is in an array as well. In the code below, I need to change the team key to teamName. My structure has to be in the same order.
let myArray = [
{
id: 1,
name: "foo",
Organization: [{ team: "value1" }, { location: "value2" }],
},
{
id: 2,
name: "foo",
Organization: [{ team: "value1" }, { location: "value2" }],
},
];
I can change the keys of the outer array like this if I want to change id to userId.
const newArray = myArray.map((item) => {
return {
userId: item.id,
};
});
But trying to change the keys in the inner list of objects for Organization becomes a problem. What is the best way to modify the inner keys?
Option 1 - lodash mapKeys
import { mapKeys } from 'lodash';
const newArray = myArray.map(item => ({
...item,
Organization: item.Organization.map(org =>
mapKeys(org, (_, key) => (key === 'team' ? 'teamName' : key))
),
}));
Option 2 - object destruction
You can destruct each Organization and reconstruct it with teamName, as long as team exists.
const newArray = myArray.map(item => ({
...item,
Organization: item.Organization.map(({ team, ...rest }) =>
Object.assign(rest, team ? { teamName: team } : {})
),
}));
Result
[
{
id: 1,
name: 'foo',
Organization: [{ teamName: 'value1' }, { location: 'value2' }],
},
{
id: 2,
name: 'foo',
Organization: [{ teamName: 'value1' }, { location: 'value2' }],
},
];
If Organization is always an array with 2 elements. Where the first element is an object with the property team, and the second element is an object with the property location. Then the following code does job.
let myArray = [{
"id": 1,
"name": "foo",
"Organization": [{"team": "value1"}, {"location": "value2"}]
}, {
"id": 2,
"name": "foo",
"Organization": [{"team": "value1"}, {"location": "value2"}]
}];
const result = myArray.map((item) => {
const [{ team: teamName }, location] = item.Organization;
return { ...item, Organization: [{ teamName }, location] };
});
console.log(result);
This answer makes use of destructuring assignment. If you don't know what this is I would highly suggest checking out the linked documentation.
Couldn't make it more simpler.
console.log(
[{
"id": 1,
"name": "foo",
"Organization": [{
"team": "value1"
}, {
"location": "value2"
}]
},
{
"id": 2,
"name": "foo",
"Organization": [{
"team": "value1"
}, {
"location": "value2"
}]
},
].reduce((a, b) => {
b.Organization[0] = {
teamName: b.Organization[0].team
}
a.push(b)
return a
}, [])
)
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)))
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);
The need is to take objects like this:
[ { "first":
{ "children" : [{ "name": "abc", "detail":"123"},
{ "name": "def", "detail":"456"}
]
}},
{ "second":
{ "children" : [{ "name": "ghi", "detail":"123"},
{ "name": "jkl", "detail":"456"}
]
}},
{ "third":
{ "children" : [{ "name": "mno", "detail":"123"},
{ "name": "pqr", "detail":"456"}
]
}},
{ "fourth":
{ "children" : [{ "name": "stu", "detail":"123"},
{ "name": "vwx", "detail":"456"}
]
}},
{ "fifth":
{ "children" : [{ "name": "yz", "detail":"123"},
{ "name": "abc", "detail":"456"}
]
}},
{ "sixth":
{ "children" : [{ "name": "def", "detail":"123"},
{ "name": "ghi", "detail":"456"}
]
}}
]
and then create a flattened array of unique values (options for a select) from the name field of the children that looks like this:
[{"value":"abc", "label":"abc"},
{"value":"def", "label":"def"},
{"value":"ghi", "label":"ghi"},
{"value":"jkl", "label":"jkl"},
{"value":"mno", "label":"mno"},
{"value":"pqr", "label":"pqr"},
{"value":"stu", "label":"stu"},
{"value":"vwx", "label":"vwx"},
{"value":"yz", "label":"yz"}
]
The code below is working, but it looks like it is inefficient because it appears to make many passes over the array:
[
...new Set(
[].concat.apply([], bases.map((base) => {
if (!base.children || base.children.length === 0) return;
return base.children}
)).map((child) => child.name)
)
].map((optName) => {return {value: optName, label: optName};})
If it is possible, how can this same result be achieved without as many iterations across the array.
Firstly, as a rule of thumb, you shouldn't worry too much about performance until you have a reason to do so.
Secondly, chaining the array prototype functions (e.g. map, forEach, filter) will require multiple iterations by design.
Thirdly, there's no reason to assume multiple iterations is slower than a single iteration if the work done within the iterations is the same anyways. I.e. incrementing an index and comparing it with an array length isn't going to be the bottleneck compared to pushing objects into arrays and check set entries.
Here's a (IMO) cleaner snippet to extract unique names from your array:
let bases = [{
children: [{
name: "abc",
detail: "123"
},
{
name: "def",
detail: "456"
}
]
}, {
children: [{
name: "abc" ,
detail: "123"
},
{
name: "xyz" ,
detail: "456"
}
]
},
{}
];
let output = bases
.flatMap(b => b.children || [])
.map(c => c.name)
.filter((v, i, a) => a.indexOf(v) === i) // filter unique values
.map(name => ({
value: name,
label: name,
}));
console.log(output);
Now if you really want to do all this in a single iteration, that too is possible, but harder to read:
let bases = [{
children: [{
name: "abc",
detail: "123"
},
{
name: "def",
detail: "456"
}
]
}, {
children: [{
name: "abc" ,
detail: "123"
},
{
name: "xyz" ,
detail: "456"
}
]
},
{}
];
let output = [];
let seenNames = {};
for (base of bases) {
if (!base.children)
continue;
for (child of base.children) {
let name = child.name;
if (seenNames[name])
continue;
seenNames[name] = true;
output.push({
value: name,
label: name,
});
}
}
console.log(output);
You could take Array#flatMap for getting a flat representation of data for using unique values and map new objects.
var data = [{ first: { children: [{ name: "abc", detail: "123" }, { name: "def", detail: "456" }] } }, { second: { children: [{ name: "ghi", detail: "123" }, { name: "jkl", detail: "456" }] } }, { third: { children: [{ name: "mno", detail: "123" }, { name: "pqr", detail: "456" }] } }, { fourth: { children: [{ name: "stu", detail: "123" }, { name: "vwx", detail: "456" }] } }, { fifth: { children: [{ name: "yz", detail: "123" }, { name: "abc", detail: "456" }] } }, { sixth: { children: [{ name: "def", detail: "123" }, { name: "ghi", detail: "456" }] } }],
result = Array.from(
new Set(data
.flatMap(Object.values)
.flatMap(({ children }) => children.map(({ name }) => name))
),
value => ({ value, label: value })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
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);