Transforming array of objects based upon key - javascript - 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);

Related

Convert key/value array

What is the best way to convert to following array. Do I need to map over it or is there some other way with Object.values etc:
[
{
key: "eyeColor",
value: "Blue/Green",
},
{
key: "eyeColor",
value: "Blue",
},
{
key: "hairColor",
value: "black",
}
];
into:
{
hairColor: ["Blond", "Brown"],
eyeColor: ["Blue"],
}
Using Array#reduce, iterate over the array while updating a Map where the key is the key properties and the value is the corresponding grouped-value properties.
Using Array#map, iterate over the above pairs and convert to objects:
const data = [ { key: "eyeColor", value: "Blue/Green" }, { key: "eyeColor", value: "Blue" }, { key: "hairColor", value: "black" } ];
const groupedPairs = [...data.reduce((map, { key, value }) => map.set(key, [...(map.get(key) ?? []), value]), new Map)];
const list = groupedPairs.map(([ prop, list ]) => ({ [prop]: list }))
console.log(list);
EDIT AFTER CHANGE OF EXPECTED OUTPUT:
const data = [ { key: "eyeColor", value: "Blue/Green" }, { key: "eyeColor", value: "Blue" }, { key: "hairColor", value: "black" } ];
const res = data.reduce((acc, { key, value }) => ({
...acc, [key]: [...(acc[key] ?? []), value]
}), {});
console.log(res);

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

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));

How to map value to key?

Map will not accept dot notation for a key, how would you map a key from an array of objects to a key in a new object? For example I would like to convert:
this
searchResults = [{ _id: 'qEJBC9gED8knEKhHF',
key: 'key1',
value: 1 },
{ _id: 'KeoMTbpuCeuQMH8LJ',
key: 'key2',
value: 5 },
{ _id: 'Foy5pXbDbtLABmCxC',
key: 'key3',
value: 4 }
]
to this
[{ _id: 'qEJBC9gED8knEKhHF',
key1: 1 },
{ _id: 'KeoMTbpuCeuQMH8LJ',
key2: 5 },
{ _id: 'Foy5pXbDbtLABmCxC',
key3: 4 }]
Example that does not work...
get_settings = function(doc) {
return {
doc.key: doc.value
}
};
currentUserSettings = searchResults.settings.map(get_settings);
You can achieve this with JS Array.Map prototype
Just use square brackets and pass in the value.
var data = [{
_id: 'qEJBC9gED8knEKhHF',
key: 'key1',
value: 1
},
{
_id: 'KeoMTbpuCeuQMH8LJ',
key: 'key2',
value: 5
},
{
_id: 'Foy5pXbDbtLABmCxC',
key: 'key3',
value: 4
}
]
var mappedData = data.map(function(item) {
return {
_id: item._id,
[item.key]: item.value
}
});
console.log(mappedData)

Setting array keys dynamically based on length

Given an array in this format:
[
[{
name: "name",
value: "My-name"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test.com"
},
{
name: "comment",
value: "my-comment"
}
],
[{
name: "name",
value: "My-name2"
},
{
name: "qty",
value: "3"
},
{
name: "url",
value: "test2.com"
}
],
[{
name: "name",
value: "My-name3"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test3.com"
},
{
name: "comment",
value: "my-comment3"
}
]
]
I'm looking to switch that to:
[
[
{ name: "My-name" },
{ qty: "1" },
{ url: "test.com" },
{ comment: "my-comment", }
],[
{ name: "My-name2" },
{ qty: "3" },
{ url: "test2.com",
],[
{ name: "My-name3", },
{ qty: "1", },
{ url: "test3.com", },
{ comment: "my-comment3", }
]
]
In other words, swapping out the array keys but maintaining the object structure within each array element.
I've tried looping over each element and can swap the keys out using something like:
newArray[iCount][item.name] = item.value;
However I'm then struggling to preserve the object order. Note that the comment field may or may not appear in the object.
With Array.map() function:
var arr = [
[{name:"name",value:"My-name"},{name:"qty",value:"1"},{name:"url",value:"test.com"},{name:"comment",value:"my-comment"}],
[{name:"name",value:"My-name2"},{name:"qty",value:"3"},{name:"url",value:"test2.com"}],
[{name:"name",value:"My-name3"},{name:"qty",value:"1"},{name:"url",value:"test3.com"},{name:"comment",value:"my-comment3"}]
],
result = arr.map(function(a){
return a.map(function(obj){
var o = {};
o[obj.name] = obj.value
return o;
});
});
console.log(result);
Check my moreBetterOutput value. I think will be better.
If you still need a result like your example in the question then you can check output value.
const input = [
[
{
name:"name",
value:"My-name"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test.com"
},
{
name:"comment",
value:"my-comment"
}
],
[
{
name:"name",
value:"My-name2"
},
{
name:"qty",
value:"3"
},
{
name:"url",
value:"test2.com"
}
],
[
{
name:"name",
value:"My-name3"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test3.com"
},
{
name:"comment",
value:"my-comment3"
}
]
]
const output = input.map(arr => arr.map(obj => ({[obj.name]: obj.value})))
const moreBetterOutput = output.map(arr => arr.reduce((acc, item, index) => {
acc[Object.keys(item)[0]] = item[Object.keys(item)[0]];
return acc;
}, {}) )
//console.log(output);
console.log(moreBetterOutput);
Another map function:
const result = array.map( subarray =>
Object.assign(...subarray.map( ({name, value}) => ({ [name] : value }) ))
);

Convert flat array to nested parent-child format

Currently, I have an array with below format:
[{
key: "a"
}, {
key: "b"
}, {
key: "c"
}, {
key: "d"
}, {
key: "e"
}]
Every element in the array is the parent of element next to it.
Required to convert it to below format:
[{
key: "a",
Nodes: [{
key: "b",
Nodes: [{
key: "c",
Nodes: [{
key: "d",
Nodes: [{
key: "e"
}]
}]
}]
}]
}]
I have achieved this, but the logic I have implemented is quite lengthy and now I want to optimize code.
So I want to know the most optimized way to do this
Using Array#reduceRight makes this simple:
const array = [{
key: "a"
}, {
key: "b"
}, {
key: "c"
}, {
key: "d"
}, {
key: "e"
}];
const nested = array.reduceRight((Nodes, obj) => {
if (Nodes) {
return [Object.assign({}, obj, { Nodes })];
} else {
return [obj];
}
}, null);
console.log(nested);

Categories

Resources