How can I create a JavaScript object from an array of objects? - javascript

I'm trying to get an array of objects into an object-format with the values as the keys of the new object.
Let's say I got this data:
const data = [
{
key: "foo",
value: "xyz",
classLabel: "Test"
},
{
key: "foo",
value: "abc",
classLabel: "Test"
},
{
key: "bar",
value: "aaa",
classLabel: "Test"
}]
And the format I want to build is like this:
const expected = {
foo: ["xyz", "abc"],
bar: ["aaa"]
}
The values are transferred to the keys and pushed into the same array for duplicate keys.
So far I only extracted the keys with:
const result = [...new Set(data.map(item => item.key))]; // ["foo", "bar"]

const data = [
{
key: "foo",
value: "xyz",
classLabel: "Test"
},
{
key: "foo",
value: "abc",
classLabel: "Test"
},
{
key: "bar",
value: "aaa",
classLabel: "Test"
}];
let expected = data.reduce((out, {key, value}) => {
out[key] = out[key] || [];
out[key].push(value);
return out;
}, {});
console.log(expected);

The following should work:
const data = [
{
key: "foo",
value: "xyz",
classLabel: "Test",
},
{
key: "foo",
value: "abc",
classLabel: "Test",
},
{
key: "bar",
value: "aaa",
classLabel: "Test",
},
];
const mapToObj = (arr) => {
let obj = {};
for (let i in arr) {
let objKey = arr[i].key;
obj[objKey]
? Object.assign(obj, { [arr[i].key]: [obj[objKey], arr[i].value] })
: Object.assign(obj, { [arr[i].key]: arr[i].value });
}
return obj;
};
console.log(mapToObj(data));

Related

Move objects in array where duplicates occur

I have an array of objects, each array has a key of name and then another array of objects:
const myArray = [ { name: "1", item: [{}] }, { name: "2", item: [{}] }, { name: "1", item: [{}] } ]
Now for example sometimes that name key will be the same, i want to be able to check if that name exists and if it does exist push the item into that array object and not into a new object.
The behaviour im getting is above but i would like:
const myArray = [ { name: "1", item: [{ item1, item2 etc }] }, { name: "2", item: [{}] }, { name: "3", item: [{}] } ]
Thanks so much in advance!
You can get the desired result using Array.reduce(), grouping by name.
If two objects in myArray share the same name, the item values are combined.
const myArray = [ { name: "1", item: [{ id: 1 }] }, { name: "2", item: [{ id: 2}] }, { name: "1", item: [{ id: 3}] } ]
const result = Object.values(myArray.reduce((acc, { name, item }) => {
acc[name] = acc[name] || { name, item: [] };
acc[name].item.push(...item);
return acc;
}, {}))
console.log('Result:', result)
.as-console-wrapper { max-height: 100% !important; }
Here's a solution using Array.prototype.reduce function.
const myArray = [ { name: "1", item: [{}] }, { name: "2", item: [{}] }, { name: "1", item: [{}] } ];
const output = myArray.reduce((acc, curr) => {
const index = acc.findIndex(pre => pre.name === curr.name);
if(index !== -1) {
acc[index].item = acc[index].item.concat(curr.item);
} else {
acc.push(curr);
}
return acc;
}, []);
console.log(output);

ES6 map.has is not a function when called in a reducer

I want to return an array of objects without any duplicate ids. If there are any, then take the first one we see. So, we should NOT see {id: "2", value: '10'}. Instead, the value should be "Italian". I have this code below, but I am getting an map.has is not a function error.
const arr1 = [{
id: "1",
value: "English"
},
{
id: "2",
value: "Italian"
}
];
const arr2 = [{
id: "2",
value: '10'
},
{
id: "3",
value: "German"
}
];
const concatArr = arr1.concat(arr2);
const mergedArr = [...concatArr.reduce((map, obj) => map.has(obj.id) ? "" : map.set(obj.id, obj), new Map()).values()];
console.log(mergedArr);
You need to always return a map not an empty string when the thing is already in the map.
const arr1 = [{
id: "1",
value: "English"
},
{
id: "2",
value: "Italian"
}
];
const arr2 = [{
id: "2",
value: '10'
},
{
id: "3",
value: "German"
}
];
const concatArr = arr1.concat(arr2);
const mergedArr = [...concatArr.reduce((map, obj) => map.has(obj.id) ? map : map.set(obj.id, obj), new Map()).values()];
console.log(mergedArr);
You can use array#reduce to uniquely identify each object with unique id in an object accumulator and then extract all values from this object using Object.values().
const arr1 = [{ id: "1", value: "English" }, { id: "2", value: "Italian" } ],
arr2 = [{ id: "2", value: '10' }, { id: "3", value: "German" } ],
result = Object.values(arr1.concat(arr2).reduce((r, o) => {
r[o.id] = r[o.id] || o;
return r;
},{}));
console.log(result);

Transforming array of objects based upon key - javascript

I am trying to figure out the most efficient method for reducing an array of objects based upon a unique key (key/values are dynamically returned in this case). I've tried to a combination of different methods using concat, map, or filter but haven't had much luck.
Original array of objects:
[
{
key: "Name",
value: "John"
},
{
key: "Company",
value: "Acme"
},
{
key: "Name",
value: "Jack"
},
{
key: "Name",
value: "Matt"
},
{
key: "Last",
value: "Test"
}
]
Desired Array:
[
{
key: "Name",
values: [
"John",
"Jack",
"Matt"
]
},
{
key: "Company",
values: [
"Acme"
]
},
{
key: "Last",
values: [
"Test"
]
}
]
Prob other ways, but a simple for loop would suffice:
const data = [{
key: "Name",
value: "John"
},
{
key: "Company",
value: "Acme"
},
{
key: "Name",
value: "Jack"
},
{
key: "Name",
value: "Matt"
},
{
key: "Last",
value: "Test"
}
]
let result = {}
for (const i in data) {
result[data[i].key] = {
key: data[i].key,
values: [
...result[data[i].key] ? result[data[i].key].values : [],
data[i].value
]
}
}
console.log(Object.values(result))
You can use reduce to build a new object using name as the keys, and then use Object.values to create the output you need from the object:
const data = [
{ key: "Name", value: "John" },
{ key: "Company", value: "Acme" },
{ key: "Name", value: "Jack" },
{ key: "Name", value: "Matt" },
{ key: "Last", value: "Test" }
];
const out = Object.values(data.reduce((acc, { key, value }) => {
// If the key doesn't exist on the object, add it
// and initialise the value object
acc[key] = acc[key] || { key, values: [] };
// Push the value from the current iteration
// into the values array
acc[key].values.push(value);
// Return the accumulator for the next iteration
return acc;
}, {}));
console.log(out);
I think reduce is the best solution here :)
const initialArray = [
{
key: "Name",
value: "John"
},
{
key: "Company",
value: "Acme"
},
{
key: "Name",
value: "Jack"
},
{
key: "Name",
value: "Matt"
},
{
key: "Last",
value: "Test"
}
];
const result = initialArray.reduce((acc, obj) => {
/* try to find object in the result array
returns index or -1 if object is missing
*/
const existingIndex = acc.findIndex(item => item.key === obj.key);
if (existingIndex > -1) {
/* object already exists, update its values array */
acc[existingIndex].values.push(obj.value);
return acc;
} else {
/* the key is first encountered, create an object in the result array */
acc.push({
key: obj.key,
values: [obj.value],
});
return acc;
}
}, []); // [] - default value is an empty array
console.log(result);

Advanced filter object in js

I'm trying filter Object of Arrays with Object but i don't have idea what can I do it.
Sample:
{
245: [
{
id: "12",
name: "test",
status: "new"
},
{
id: "15",
name: "test2",
status: "old"
},
{
id: "12",
name: "test2",
status: "old"
}],
2815: [
{
id: "19",
name: "test",
status: "new"
},
{
id: "50",
name: "test2",
status: "old"
},
{
id: "120",
name: "test2",
status: "new"
}]
}
Need filter if status = "new" but struct must not change:
{
245: [{
id: "12",
name: "test",
status: "new"
}],
2815: [{
id: "19",
name: "test",
status: "new"
},
{
id: "120",
name: "test2",
status: "new"
}]
}
Loop over entries and create a new object with filtered values
const obj = {
245:[
{id:"12",name:"test",status:"new"},{id:"15",name:"test2",status:"old"},{id:"12",name:"test2",status:"old"}],
2815:[
{id:"19",name:"test",status:"new"},{id:"50",name:"test2",status:"old"},{id:"120",name:"test2",status:"new"}]
}
console.log(filter(obj, item => item.status === "new"))
function filter(obj, pred) {
return Object.fromEntries(Object.entries(obj).map(([name, value]) => [name, value.filter(pred)]))
}
You need to map over the object keys and then over the array elements to filter out the final result
var obj = {
245:[
{id:"12",name:"test",status:"new"},{id:"15",name:"test2",status:"old"},{id:"12",name:"test2",status:"old"}],
2815:[
{id:"19",name:"test",status:"new"},{id:"50",name:"test2",status:"old"},{id:"120",name:"test2",status:"new"}]
}
var res = Object.entries(obj).reduce((acc, [key, value]) => {
acc[key] = value.filter(item => item.status === "new");
return acc;
}, {})
console.log(res);
you can do it for this specific case like this:
const myObj = {
245:[
{id:"12",name:"test",status:"new"},
{id:"15",name:"test2",status:"old"},
{id:"12",name:"test2",status:"old"}
],
2815:[
{id:"19",name:"test",status:"new"},
{id:"50",name:"test2",status:"old"},
{id:"120",name:"test2",status:"new"}
]
};
const filteredObj = filterMyObj(myObj);
console.log(filteredObj);
function filterMyObj(myObj){
const myObjCopy = {...myObj};
for (const key in myObjCopy){
const myArrCopy = [...myObjCopy[key]];
myObjCopy[key] = myArrCopy.filter(item => item.status == "new");
}
return myObjCopy;
}
You can do it with filter :
for(let key in obj){
obj[key] = obj[key].filter(el => el.status == "new")
}

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