Count array inside array of object - javascript

Suppose I have an array of Object as:
Expected O/P : {alpha:4, beta:8}
For this I tried as:
const apple = [{
name: 'alpha',
details: [{
"attachment": [123, 456]
}, {
"attachment": [1454, 1992]
}]
},
{
name: 'beta',
details: [{
"attachment": ["12", 189]
}, {
"attachment": ["maggi", 1890, 2000]
}, {
"attachment": [1990, 2001, 2901]
}]
}
];
const appleDetails = [];
for (let i = 0; i < apple.length; i++) {
for (let j = 0; j < apple[i].details.length; j++) {
const {
name
} = apple[i];
const {
attachment
} = apple[i].details[j]
appleDetails.push({
name,
attachment
})
}
}
let appleData = [];
appleDetails.forEach(({
name,
attachment
}) => {
attachment.forEach((attatchId) => {
appleData.push({
name,
_id: attatchId
})
})
})
const eachAppleCount = appleData.reduce((acc, item) => {
const key = item.name
if (!acc.hasOwnProperty(key)) {
acc[key] = 0
}
acc[key] += 1
return acc
}, {})
console.log(eachAppleCount);
This gives the O/P as required i.e. : {alpha:4, beta:8}
But the process involved is too much, is there any more efficient way such that,by using:
const apple = [{
name: 'alpha',
details: [{
"attachment": [123, 456]
}, {
"attachment": [1454, 1992]
}]
}, {
name: 'beta',
details: [{
"attachment": ["12", 189]
}, {
"attachment": ["maggi", 1890, 2000]
}, {
"attachment": [1990, 2001, 2901]
}]
}];
We can count the value for each apple name. If anyone needs any further information please do let me know.

This will do the work
const apple = [{
name: 'alpha',
details: [{
"attachment": [123, 456]
}, {
"attachment": [1454, 1992]
}]
}, {
name: 'beta',
details: [{
"attachment": ["12", 189]
}, {
"attachment": ["maggi", 1890, 2000]
}, {
"attachment": [1990, 2001, 2901]
}]
}];
const result = apple.reduce((acc, curr) => {
if (acc[curr.name] === undefined) acc[curr.name] = 0;
curr.details.forEach((d) => (acc[curr.name] += d.attachment.length));
return acc;
}, {});
console.log(result);

Try like below.
Array.prototype.map() - The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
Create new object as {[a.name]: values} where we use [a.name] inside [] so value from a.name will be set as object key.
To get value we use Array.prototype.flatMap() - The flatMap() method returns a new array formed by applying a given callback function to each element of the array, and then flattening the result by one level.
Get all attachments with a.details.flatMap(d => d.attachment) and use .length to get total count as value.
map will return result in format of array of objects.
Use Array.prototype.reduce() with spread syntax (...) to club array of objects into single object.
const apple = [
{ name: 'alpha', details: [{"attachment": [ 123, 456 ]}, {"attachment": [1454, 1992 ]} ]},
{name: 'beta', details: [{"attachment": [ "12", 189 ]},{"attachment": ["maggi", 1890,2000 ]}, {"attachment": [1990, 2001,2901 ]} ]}
];
// map will return result in format of array of objects
let resultArr = apple.map(a => ({[a.name]: a.details.flatMap(d => d.attachment).length }));
// use reduce with spread syntax (...) to club array of objects into single object.
let result = resultArr.reduce((i, a) => ({...a, ...i}), {});
console.log(result);

Related

Sort array by number from other array

I have two arrays and I want to sort first one based on some values from another array:
const items = [
['music', ['arr']],
['movies', ['arr']],
['quizes', ['arr']],
['series', ['arr']]
];
const categories = [
{ name: "music", priority: 3},
{ name: "movies", priority: 2},
{ name: "quizes", priority: 5},
{ name: "series", priority: 1},
{ name: "sports", priority: 4},
];
I want to sort my first array, by property 'priority' from my second array -> from the biggest one.
Like this:
const expectedResult = [
['quizes', ['arr']],
['music', ['arr']]
['movies', ['arr']],
['series', ['arr']],
];
This is what I tried but without success.
const sorted = items.sort((a,b) => {
const [aKey, aVal] = a;
const [bKey, bVal] = b;
const prio = categories.filter(c => c.name === aKey)[0];
// not sure how to use this prio
return aKey.priority - bKey.priority;
})
You were very close, you just needed to grab b's priority (and also use the priority property). find rather than filter is a good choice:
const sorted = items.sort((a,b) => {
const [aKey] = a;
const [bKey] = b;
const aPriority = categories.find(cat => cat.name === aKey).priority;
const bPriority = categories.find(cat => cat.name === bKey).priority;
return bPriority - aPriority;
});
Live Example
const items = [
["music", ["arr"]],
["movies", ["arr"]],
["quizes", ["arr"]],
["series", ["arr"]]
];
const categories = [
{ name: "music", priority: 3},
{ name: "movies", priority: 2},
{ name: "quizes", priority: 5},
{ name: "series", priority: 1},
{ name: "sports", priority: 4},
];
const sorted = items.sort((a,b) => {
const [aKey] = a;
const [bKey] = b;
const aPriority = categories.find(cat => cat.name === aKey).priority;
const bPriority = categories.find(cat => cat.name === bKey).priority;
return bPriority - aPriority;
});
console.log(sorted);
.as-console-wrapper {
max-height: 100% !important;
}
But repeatedly traversing that array of categories isn't a good idea if items is long. Instead, make a Map of key to priority, then use that:
const catPriorityMap = new Map(categories.map(({name, priority}) => [name, priority]));
const sorted = items.sort((a,b) => {
const [aKey] = a;
const [bKey] = b;
const aPriority = catPriorityMap.get(aKey);
const bPriority = catPriorityMap.get(bKey);
return bPriority - aPriority;
});
Live Example
const items = [
["music", ["arr"]],
["movies", ["arr"]],
["quizes", ["arr"]],
["series", ["arr"]]
];
const categories = [
{ name: "music", priority: 3},
{ name: "movies", priority: 2},
{ name: "quizes", priority: 5},
{ name: "series", priority: 1},
{ name: "sports", priority: 4},
];
const catPriorityMap = new Map(categories.map(({name, priority}) => [name, priority]));
const sorted = items.sort((a,b) => {
const [aKey] = a;
const [bKey] = b;
const aPriority = catPriorityMap.get(aKey);
const bPriority = catPriorityMap.get(bKey);
return bPriority - aPriority;
});
console.log(sorted);
.as-console-wrapper {
max-height: 100% !important;
}
Lookup in a Map is done in sublinear time, whereas finding somethign in an array is done in linear time.
You can use sort() method and check the priority from the categories
const items = [
['music', ['arr']],
['movies', ['arr']],
['quizes', ['arr']],
['series', ['arr']],
];
const categories = [
{ name: 'music', priority: 3 },
{ name: 'movies', priority: 2 },
{ name: 'quizes', priority: 5 },
{ name: 'series', priority: 1 },
{ name: 'sports', priority: 4 },
];
const result = items.sort(([a], [b]) => {
const aPriority = categories.find(({ name }) => name === a).priority;
const bPriority = categories.find(({ name }) => name === b).priority;
return bPriority - aPriority;
});
console.log(result);
Learn more about sort() here.
You really do not want to use find() or filter() inside of the sort method because it is expensive. On every iteration, you are looking up the data in the array. So you are looping a lot. There is better ways to get the index.
Easiest thing is to make a lookup object so you are not having to search the other array over and over for a match. So if you can change categories to an object from the start, it will make your life so much easier.
In the sort I added max value in case the key is not defined. Now this would backfire if you had a value of zero since it is just a truthy check.
const items = [
['music', ['arr']],
['movies', ['arr']],
['quizes', ['arr']],
['series', ['arr']]
];
const categories = {
music: 3,
movies: 2,
quizes: 5,
series: 1,
sports: 4,
};
items.sort(([keyA], [keyB]) => (categories[keyA] || Number.MAX_VALUE) - (categories[keyB] || Number.MAX_VALUE));
console.log(items);
If you can not make the object look like that and you have to use the array, you can convert it from the array to an object. That can be done a few ways. I like to use reduce.
const items = [
['music', ['arr']],
['movies', ['arr']],
['quizes', ['arr']],
['series', ['arr']]
];
const categories = [
{ name: "music", priority: 3},
{ name: "movies", priority: 2},
{ name: "quizes", priority: 5},
{ name: "series", priority: 1},
{ name: "sports", priority: 4},
];
const lookup = categories.reduce((acc, obj) => ({...acc, [obj.name]: obj.priority}), {});
items.sort(([keyA], [keyB]) => (lookup[keyA] || Number.MAX_VALUE) - (lookup[keyB] || Number.MAX_VALUE));
console.log(items);
Now if you are sure that all of the keys will exist in the categories, you can drop the max value
items.sort(([keyA], [keyB]) => lookup[keyA] - lookup[keyB];
You could take an object for the order and sort by delta.
const
items = [['music', ['arr']], ['movies', ['arr']], ['quizes', ['arr']], ['series', ['arr']]],
categories = [{ name: "music", priority: 3 }, { name: "movies", priority: 2 }, { name: "quizes", priority: 5 }, { name: "series", priority: 1 }, { name: "sports", priority: 4 }],
order = Object.fromEntries(
categories.map(({ name, priority }) => [name, priority])
);
items.sort(([a], [b]) => order[a] - order[b]);
console.log(items);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to merge two array of objects by key AND keep unique keys in a single array of objects?

I want to merge two array of objects where objects with the same ID will merge properties and objects with unique IDs will be its own object in the merged array. The following code does the first part where similar IDs will merge but how do I keep objects with unique ids from arr2 in the merged array and have it work with arrays of varying lengths?
Expected output:
[
{
"id": "1",
"date": "2017-01-24",
"name": "test"
},
{
"id": "2",
"date": "2017-01-22",
"bar": "foo"
}
{ "id": "3",
"foo": "bar",
}
]
The code:
let arr1 = [{
id: '1',
createdDate: '2017-01-24'
},
{
id: '2',
createdDate: '2017-01-22'
},
];
let arr2 = [{
id: '1',
name: 'test'
},
{
id: '3',
foo: 'bar'
},
{
id: '2',
bar: 'foo'
},
];
let merged = [];
for (let i = 0; i < arr1.length; i++) {
merged.push({
...arr1[i],
...arr2.find((itmInner) => itmInner.id === arr1[i].id),
},
);
}
console.log(merged);
Iterate over the larger array, the one that contains the smaller array, instead:
let arr1=[{id:"1",createdDate:"2017-01-24"},{id:"2",createdDate:"2017-01-22"}],arr2=[{id:"1",name:"test"},{id:"3",foo:"bar"},{id:"2",bar:"foo"}];
const merged = arr2.map(item => ({
...arr1.find(({ id }) => id === item.id),
...item
}));
console.log(merged);
(if order matters, you can sort if afterwards too)
If you don't know in advance which one / if one will contain the other, then use an object to index the merged objects by IDs first:
let arr1=[{id:"1",createdDate:"2017-01-24"},{id:"2",createdDate:"2017-01-22"}],arr2=[{id:"1",name:"test"},{id:"3",foo:"bar"},{id:"2",bar:"foo"}];
const resultObj = Object.fromEntries(
arr1.map(
item => [item.id, { ...item }]
)
);
for (const item of arr2) {
if (!resultObj[item.id]) {
resultObj[item.id] = item;
} else {
Object.assign(resultObj[item.id], item);
}
}
const merged = Object.values(resultObj);
console.log(merged);
You can create new object that contains the elems of arr1 and arr2 group by id key as follows and the merged array will be stored on object values.
You can get object values using Object.values func.
let arr1 = [{
id: '1',
createdDate: '2017-01-24'
},
{
id: '2',
createdDate: '2017-01-22'
},
];
let arr2 = [{
id: '1',
name: 'test'
},
{
id: '3',
foo: 'bar'
},
{
id: '2',
bar: 'foo'
},
];
const groupById = {};
for (let i = 0; i < Math.min(arr1.length, arr2.length); i ++) {
if (arr1[i]) {
groupById[arr1[i].id] = { ...groupById[arr1[i].id], ...arr1[i] };
}
if (arr2[i]) {
groupById[arr2[i].id] = { ...groupById[arr2[i].id], ...arr2[i] };
}
}
const merged = Object.values(groupById);
console.log(merged);
You could take a single loop approach by storing the objects in a hash table, sorted by id.
const
mergeTo = (target, objects = {}) => o => {
if (!objects[o.id]) target.push(objects[o.id] = {});
Object.assign(objects[o.id], o);
},
array1 = [{ id: '1', createdDate: '2017-01-24' }, { id: '2', createdDate: '2017-01-22' }],
array2 = [{ id: '1', name: 'test' }, { id: '3', foo: 'bar' }, { id: '2', bar: 'foo' }],
merged = [],
merge = mergeTo(merged);
array1.forEach(merge);
array2.forEach(merge);
console.log(merged);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A different approach could be merged the two array as is, and then "squash" it:
let arr1 = [{
id: '1',
createdDate: '2017-01-24'
},
{
id: '2',
createdDate: '2017-01-22'
},
];
let arr2 = [{
id: '1',
name: 'test'
},
{
id: '3',
foo: 'bar'
},
{
id: '2',
bar: 'foo'
},
];
let merged = [...arr1, ...arr2].reduce(
(acc, {id, ...props}) =>
(acc.set(id, {...(acc.get(id) || {}), ...props}), acc), new Map());
console.log([...merged].map( ([id, props]) => ({id, ...props}) ))
Notice that you might not need the last line, it used just to obtain the format you want to, since the above reduce is using a Map as accumulator, you can already access to everything with just merged.get("1").createdDate for example (where "1" is the id).
Since you're operating on one array by merging them at the beginning, you don't care about the length of them or even which one contains more elements. You can also have several arrays instead of just two, it doesn't matter.
What it matters is the order: if more than one array contains the same property for the same "id", the value you'll get is the value from the most recent array added (in the example above, would be arr2).
You can write a function to reduce the arrays to an object and then extract the value from that object which will return the values that you want. You can see the code below:
let arr1 = [
{
id: '1',
createdDate: '2017-01-24',
},
{
id: '2',
createdDate: '2017-01-22',
},
];
let arr2 = [
{
id: '1',
name: 'test',
},
{
id: '3',
foo: 'bar',
},
{
id: '2',
bar: 'foo',
},
];
function merge(arr1 = [], arr2 = []) {
return Object.values(
arr1.concat(arr2).reduce(
(acc, curr) => ({
...acc,
[curr.id]: { ...(acc[curr.id] ?? {}), ...curr },
}),
{}
)
);
}
const merged = merge(arr1, arr2);
Output:
[
{
"id": "1",
"createdDate": "2017-01-24",
"name": "test"
},
{
"id": "2",
"createdDate": "2017-01-22",
"bar": "foo"
},
{
"id": "3",
"foo": "bar"
}
]

In array of objects count grouped values by certain key

I have a following data:
const data2 = [
{
App: "testa.com",
Name: "TEST A",
Category: "HR",
Employees: 7
},
{
App: "testd.com",
Name: "TEST D",
Category: "DevOps",
Employees: 7
},
{
App: "teste.com",
Name: "TEST E",
Category: "DevOps",
Employees: 7
},
{
App: "testf.com",
Name: "TEST F",
Category: "Business",
Employees: 7
}
]
I want to get the count of distinct categories: Right now I am getting the list of all distinct categories but I'm unable to compute their count.
Following snippet give me the Distinct Category:
let uniqueCategory = [];
for(let i = 0; i < result.data.length; i++){
if(uniqueCategory.indexOf(result.data[i].Category) === -1){
uniqueCategory.push(result.data[i].Category);
}
}
What changes should I make to get the Counts of those Categories in the uniqueCategory - something like following:
uniqueCategory = [
{Category: "DevOps", count: 5},
{Category: "Business", count: 4},
....
{}
]
Your approach implies looping your source array (with .indexOf()) every iteration of for(..-loop. That will slow down unnecessarily look up process.
Instead, you may employ Array.prototype.reduce() to traverse your source array and build up the Map, having Category as a key and object of desired format as a value, then extract Map.prototype.values() into resulting array.
That will perform much faster and scale better.
const src = [{App:"testa.com",Name:"TEST A",Category:"HR",Employees:7},{App:"testd.com",Name:"TEST D",Category:"DevOps",Employees:7},{App:"teste.com",Name:"TEST E",Category:"DevOps",Employees:7},{App:"testf.com",Name:"TEST F",Category:"Business",Employees:7}],
result = [...src
.reduce((r, {Category}) => {
const cat = r.get(Category)
cat ? cat.count ++ : r.set(Category, {Category, count: 1})
return r
}, new Map)
.values()
]
console.log(result)
.as-console-wrapper{min-height:100%;}
The easiest way to do it is to use Array.prototype.reduce
const arr = [ ... ];
const output = arr.reduce((result, obj) => {
if (!result[obj.category]) {
result[obj.category] = 0;
}
result[obj.category]++;
return result;
}, {});
console.log(output); // this should log the similar output you want
Here's another alternative using .map and Set:
const src = [
{
App: "testa.com",
Name: "TEST A",
Category: "HR",
Employees: 7
},
{
App: "testd.com",
Name: "TEST D",
Category: "DevOps",
Employees: 7
},
{
App: "teste.com",
Name: "TEST E",
Category: "DevOps",
Employees: 7
},
{
App: "testf.com",
Name: "TEST F",
Category: "Business",
Employees: 7
}
];
const categories = src.map(obj => obj.Category);
const distinctCategories = [...new Set(categories)];
console.log(distinctCategories.length);

Converting object array to hierarchical data structure

I have an array of objects and I would like to convert it into a different array. Each object in the original array has a category key and I would like the final result to group objects by category. I am trying to use the reduce method to do this but can not make sense of the examples I have found.
original array:
[
{category: "film", title: "toy story"},
{category: "film", title:"harry potter"},
{category: "tv", title:"seinfeld"}
]
desired result:
[
{
category: "film",
children: [
{title: "toy story"},
{title: "harry potter"}
],
}
{
category: "tv",
children: [
{title: 'seinfeld' }
]
}
]
I am trying to use d3 to create some graphs and the data needs to be sorted in a hierarchical structure. More on that here, https://github.com/d3/d3-hierarchy/blob/v1.1.9/README.md#hierarchy
Use reduce function,
first try to look if the item already exists in the array if yes, use the found index to locate the element and append to its children, if not add the initial item to the children array.
const input = [{
category: "film",
title: "toy story"
},
{
category: "film",
title: "harry potter"
},
{
category: "tv",
title: "seinfeld"
}
]
const result = input.reduce((acc, x) => {
const index = acc.findIndex(y => y.category === x.category)
if (index >= 0) {
acc[index].children.push({
title: x.title
})
} else {
acc = [...acc, {
category: x.category,
children: [{
title: x.title
}]
}]
}
return acc;
}, []);
console.log(result)
For not having child duplicates:
const result = input.reduce((acc, x) => {
const index = acc.findIndex(y => y.category === x.category)
const indexTitle = acc[index] && acc[index].children.findIndex(y => y.title === x.title)
if (index >= 0) {
if (indexTitle !== 0) {
acc[index].children.push({
title: x.title
})
}
} else {
acc = [...acc, {
category: x.category,
children: [{
title: x.title
}]
}]
}
return acc;
}, []);
You can use a reduce function:
const arr = [
{category: "film", title: "toy story"},
{category: "film", title: "harry potter"},
{category: "tv", title: "seinfeld"}
]
const arrByCategory = arr.reduce((acc, i) => {
// Check if the category already exist in the new array
const elIdx = acc.findIndex(_ => _.category === i.category);
const elExist = elIdx > -1;
if(elExist) {
// If the category exist, we just add the title to the children list
return acc[elIdx].children.push({title: i.title})
} else {
// If the category does not exist we create it and add the initial title in the children list
return acc.concat({
category: i.category,
children: [{ title: i.title }]
})
}
},[])
To give you a better understanding of the reduce function, here is a simple example:
const array = [1, 3, 6, 2, 5]
const sum = array.reduce((acc, i) => {
return acc + i;
}, 0)
console.log(sum) // 17

Merge JavaScript objects in array with same key

What is the best way to merge array contents from JavaScript objects sharing a key in common?
How can array in the example below be reorganized into output? Here, all value keys (whether an array or not) are merged into all objects sharing the same name key.
var array = [
{
name: "foo1",
value: "val1"
}, {
name: "foo1",
value: [
"val2",
"val3"
]
}, {
name: "foo2",
value: "val4"
}
];
var output = [
{
name: "foo1",
value: [
"val1",
"val2",
"val3"
]
}, {
name: "foo2",
value: [
"val4"
]
}
];
Here is one option:-
var array = [{
name: "foo1",
value: "val1"
}, {
name: "foo1",
value: ["val2", "val3"]
}, {
name: "foo2",
value: "val4"
}];
var output = [];
array.forEach(function(item) {
var existing = output.filter(function(v, i) {
return v.name == item.name;
});
if (existing.length) {
var existingIndex = output.indexOf(existing[0]);
output[existingIndex].value = output[existingIndex].value.concat(item.value);
} else {
if (typeof item.value == 'string')
item.value = [item.value];
output.push(item);
}
});
console.dir(output);
Here is another way of achieving that goal:
var array = [{
name: "foo1",
value: "val1"
}, {
name: "foo1",
value: [
"val2",
"val3"
]
}, {
name: "foo2",
value: "val4"
}];
var output = array.reduce(function(o, cur) {
// Get the index of the key-value pair.
var occurs = o.reduce(function(n, item, i) {
return (item.name === cur.name) ? i : n;
}, -1);
// If the name is found,
if (occurs >= 0) {
// append the current value to its list of values.
o[occurs].value = o[occurs].value.concat(cur.value);
// Otherwise,
} else {
// add the current item to o (but make sure the value is an array).
var obj = {
name: cur.name,
value: [cur.value]
};
o = o.concat([obj]);
}
return o;
}, []);
console.log(output);
2021 version
Using reduce to aggregate data.
Using logical nullish assignment only assigns if acc[name] is nullish (null or undefined).
Using Array.isArray to determines whether the passed value is an Array.
var arrays = [{ name: "foo1",value: "val1" }, {name: "foo1", value: ["val2", "val3"] }, {name: "foo2",value: "val4"}];
const result = arrays.reduce((acc, {name, value}) => {
acc[name] ??= {name: name, value: []};
if(Array.isArray(value)) // if it's array type then concat
acc[name].value = acc[name].value.concat(value);
else
acc[name].value.push(value);
return acc;
}, {});
console.log(Object.values(result));
Using lodash
var array = [{name:"foo1",value:"val1"},{name:"foo1",value:["val2","val3"]},{name:"foo2",value:"val4"}];
function mergeNames (arr) {
return _.chain(arr).groupBy('name').mapValues(function (v) {
return _.chain(v).pluck('value').flattenDeep();
}).value();
}
console.log(mergeNames(array));
Here is a version using an ES6 Map:
const arrays = [{ name: "foo1",value: "val1" }, {name: "foo1", value: ["val2", "val3"] }, {name: "foo2",value: "val4"}];
const map = new Map(arrays.map(({name, value}) => [name, { name, value: [] }]));
for (let {name, value} of arrays) map.get(name).value.push(...[value].flat());
console.log([...map.values()]);
Use lodash "uniqWith". As shown below
let _ = require("lodash");
var array = [
{ name: "foo1", value: "1" },
{ name: "foo1", value: "2" },
{ name: "foo2", value: "3" },
{ name: "foo1", value: "4" }
];
let merged = _.uniqWith(array, (pre, cur) => {
if (pre.name == cur.name) {
cur.value = cur.value + "," + pre.value;
return true;
}
return false;
});
console.log(merged);
// output: [{ name: "foo1", value: "1,2,4" }, { name: "foo2", value: "3" }];
Using reduce:
var mergedObj = array.reduce((acc, obj) => {
if (acc[obj.name]) {
acc[obj.name].value = acc[obj.name].value.isArray ?
acc[obj.name].value.concat(obj.value) :
[acc[obj.name].value].concat(obj.value);
} else {
acc[obj.name] = obj;
}
return acc;
}, {});
let output = [];
for (let prop in mergedObj) {
output.push(mergedObj[prop])
}
It's been a while since this question was asked, but I thought I'd chime in as well. For functions like this that execute a basic function you'll want to use over and over, I prefer to avoid longer-written functions and loops if I can help it and develop the function as a one-liner using shallow Array.prototype functions like .map() and some other ES6+ goodies like Object.entries() and Object.fromEntries(). Combining all these, we can execute a function like this relatively easily.
First, I take in however many objects you pass to the function as a rest parameter and prepend that with an empty object we'll use to collect all the keys and values.
[{}, ...objs]
Next, I use the .map() Array prototype function paired with Object.entries() to loop through all the entries of each object, and any sub-array elements each contains and then either set the empty object's key to that value if it has not yet been declared, or I push the new values to the object key if it has been declared.
[{},...objs].map((e,i,a) => i ? Object.entries(e).map(f => (a[0][f[0]] ? a[0][f[0]].push(...([f[1]].flat())) : (a[0][f[0]] = [f[1]].flat()))) : e)[0]
Finally, to replace any single-element-arrays with their contained value, I run another .map() function on the result array using both Object.entries() and Object.fromEntries(), similar to how we did before.
let getMergedObjs = (...objs) => Object.fromEntries(Object.entries([{},...objs].map((e,i,a) => i ? Object.entries(e).map(f => (a[0][f[0]] ? a[0][f[0]].push(...([f[1]].flat())) : (a[0][f[0]] = [f[1]].flat()))) : e)[0]).map(e => e.map((f,i) => i ? (f.length > 1 ? f : f[0]) : f)));
This will leave you with the final merged object, exactly as you prescribed it.
let a = {
a: [1,9],
b: 1,
c: 1
}
let b = {
a: 2,
b: 2
}
let c = {
b: 3,
c: 3,
d: 5
}
let getMergedObjs = (...objs) => Object.fromEntries(Object.entries([{},...objs].map((e,i,a) => i ? Object.entries(e).map(f => (a[0][f[0]] ? a[0][f[0]].push(...([f[1]].flat())) : (a[0][f[0]] = [f[1]].flat()))) : e)[0]).map(e => e.map((f,i) => i ? (f.length > 1 ? f : f[0]) : f)));
getMergedObjs(a,b,c); // { a: [ 1, 9, 2 ], b: [ 1, 2, 3 ], c: [ 1, 3 ], d: 5 }
Try this:
var array = [{name:"foo1",value:"val1"},{name:"foo1",value:["val2","val3"]},{name:"foo2",value:"val4"},{name:"foo2",value:"val5"}];
for(var j=0;j<array.length;j++){
var current = array[j];
for(var i=j+1;i<array.length;i++){
if(current.name = array[i].name){
if(!isArray(current.value))
current.value = [ current.value ];
if(isArray(array[i].value))
for(var v=0;v<array[i].value.length;v++)
current.value.push(array[i].value[v]);
else
current.value.push(array[i].value);
array.splice(i,1);
i++;
}
}
}
function isArray(myArray) {
return myArray.constructor.toString().indexOf("Array") > -1;
}
document.write(JSON.stringify(array));
This work too !
var array = [
{
name: "foo1",
value: "val1",
},
{
name: "foo1",
value: ["val2", "val3"],
},
{
name: "foo2",
value: "val4",
},
];
let arr2 = [];
array.forEach((element) => { // remove duplicate name
let match = arr2.find((r) => r.name == element.name);
if (match) {
} else {
arr2.push({ name: element.name, value: [] });
}
});
arr2.map((item) => {
array.map((e) => {
if (e.name == item.name) {
if (typeof e.value == "object") { //lets map if value is an object
e.value.map((z) => {
item.value.push(z);
});
} else {
item.value.push(e.value);
}
}
});
});
console.log(arr2);
const exampleObj = [{
year: 2016,
abd: 123
}, {
year: 2016,
abdc: 123
}, {
year: 2017,
abdcxc: 123
}, {
year: 2017,
abdcxcx: 123
}];
const listOfYears = [];
const finalObj = [];
exampleObj.map(sample => {    
listOfYears.push(sample.year);
});
const uniqueList = [...new Set(listOfYears)];
uniqueList.map(list => {   
finalObj.push({
year: list
});
});
exampleObj.map(sample => {    
const sampleYear = sample.year;  
finalObj.map((obj, index) => {     
if (obj.year === sampleYear) {        
finalObj[index] = Object.assign(sample, obj);       
}  
}); 
});
The final object be [{"year":2016,"abdc":123,"abd":123},{"year":2017,"abdcxcx":123,"abdcxc":123}]
const array = [{ name: "foo1", value: "val1" }, {name: "foo1", value: ["val2", "val3"] }, {name: "foo2", value: "val4"}];
const start = array.reduce((object, {name}) => ({...object, [name]: []}), {});
const result = array.reduce((object, {name, value}) => ({...object, [name]: [object[name], [value]].flat(2)}), start);
const output = Object.entries(result).map(([name, value]) => ({name: name, value: value}));
console.log(output);
try this :
var array = [
{
name: "foo1",
value: "val1"
}, {
name: "foo1",
value: [
"val2",
"val3"
]
}, {
name: "foo2",
value: "val4"
}
];
var output = [
{
name: "foo1",
value: [
"val1",
"val2",
"val3"
]
}, {
name: "foo2",
value: [
"val4"
]
}
];
bb = Object.assign( {}, array, output );
console.log(bb) ;
A much more easier approach is this 2022:
var array = [
{
name: "foo1",
value: "val1"
}, {
name: "foo1",
value: [
"val2",
"val3"
]
}, {
name: "foo2",
value: "val4"
}
];
var output = [
{
name: "foo1",
value: [
"val1",
"val2",
"val3"
]
},
{
name: "foo2",
value: [
"val4"
]
}
];
function mergeBasedOnKey(list){
let c = Object.values(list.reduce((a, b) => {
a[b.name] = a[b.name] || {name: b.name, value: []}
if(typeof(b['value']) == "string"){
a[b.name].value.push(b['value'])
}
else{
a[b.name].value = [...a[b.name].value, ...b.value]
}
return a
}, {}))
return c
}
let ans = mergeBasedOnKey(array)
console.log(ans)
I was looking for a quick, almost "one-liner" answer in this thread, provided that this is a trivial but common exercise.
I couldn't find any for my like. The other answers are fine but I am not much into boilerplate.
So, let me add one, then:
o = array.reduce((m,{name:n,value:v})=>({...m,[n]:[...m[n]||[],v].flat(1)}),{})
output = Object.entries(o).map(([n,v])=>({name:n,value:v}))
var array = [
{ name: "foo1", value: "val1"},
{ name: "foo1", value: ["val2","val3"] },
{ name: "foo2", value: "val4" }
]
o=array.reduce((m,{name:n,value:v})=>({...m,[n]:[...m[n]||[],v].flat(1)}),{})
output=Object.entries(o).map(([n,v])=>({name:n,value:v}))
console.log(output)

Categories

Resources