I have and array of objects which I would like to transform into single js object.
The array contains target value and array of keys.
Note: I want this to work in vanilla js without importing scripts.
The source array:
[
{ value: 'res-1', keys: [ 'first' ] },
{ value: 'res-2', keys: [ 'second', 'deeperOne' ] },
{ value: 'res-3', keys: [ 'second', 'deeperTwo' ] },
{ value: 'res-4', keys: [ 'second', 'deeperThree', 'moreDeeper' ] },
{ value: 'res-5', keys: [ 'third' ]}
]
Desirable result (object):
{
first: 'res-1',
second: {
deeperOne: 'res-2',
deeperTwo: 'res-3',
deeperThree: {
moreDeeper: 'res-4'
}
},
third: 'res-5'
}
cmgchess gave answer to the question with a comment. Here is the code!
const array = [
{ value: 'res-1', keys: [ 'first' ] },
{ value: 'res-2', keys: [ 'second', 'deeperOne' ] },
{ value: 'res-3', keys: [ 'second', 'deeperTwo' ] },
{ value: 'res-4', keys: [ 'second', 'deeperThree', 'moreDeeper' ] },
{ value: 'res-5', keys: [ 'third' ]}
]
const set = (obj, path, value) => {
path.reduce((acc, key, i) => {
if (acc[key] === undefined) acc[key] = {}
if (i === path.length - 1) acc[key] = value
return acc[key]
}, obj)
}
let object = {}
array.forEach(({value, keys}) => set(object, keys, value))
console.log(object)
Related
I have an array like this :
[
{
name: 'foo',
nestedArray: [
{
name: 'bar',
nestedArray: []
}
]
}
]
What's the best way to have a flatten array that looks like this ?
[
{
name: 'foo',
nestedArray: [
{
name: 'bar',
nestedArray: []
}
]
},
{
name: ' bar',
nestedArray: []
}
]
You can try by iterating input array and moving out nested array objects into outer one. I hope this will work as per your expectation :
// Input array
const inputArray = [
{
name: 'foo',
nestedArray: [
{
name: 'bar',
nestedArray: []
}
]
}
];
// Result array
const res = [];
// Iterating input array and moving out nested array objects into outer one.
inputArray.forEach((obj) => {
res.push(obj);
obj.nestedArray.forEach((nestedObj) => {
res.push(nestedObj);
});
});
// Assigning result
console.log(res);
Given a JavaScript object, how can I convert it into an array of objects (each with key, value)?
Example:
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' }
resulting like:
[
{ key: 'firstName', value: 'John' },
{ key: 'lastName', value: 'Doe' },
{ key: 'email', value: 'john.doe#gmail.com' }
]
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' }
var output = Object.entries(data).map(([key, value]) => ({key,value}));
console.log(output);
Inspired By this post
Using map function
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' };
var result = Object.keys(data).map(key => ({ key, value: data[key] }));
console.log(result);
You can just iterate over the object's properties and create a new object for each of them.
var data = { firstName: 'John', lastName: 'Doe', email: 'john.doe#gmail.com' };
var result = [];
for(var key in data)
{
if(data.hasOwnProperty(key))
{
result.push({
key: key,
value: data[key]
});
}
}
The previous answer lead me to think there is a better way...
Object.keys(data).map(function(key) {
return { key, value: data[key] };
});
or in ES6 using arrow functions:
Object.keys(data).map((key) => ({ key, value: data[key] }));
Just make your life easier and use es6 syntax with a map
var output = Object.keys(data).map(key => {
return {
key: key,
value: data[key]
};
})
var result = [];
for(var k in data) result.push({key:k,value:data[k]});
Or go wild and make the key and value keys customizable:
module.exports = function objectToKeyValueArray(obj, keyName = 'key', valueName = 'value') {
return Object
.keys(obj)
.filter(key => Object.hasOwnProperty.call(obj, key))
.map(key => {
const keyValue = {};
keyValue[keyName] = key;
keyValue[valueName] = obj[key];
return keyValue;
});
};
An alternative method for doing this that works on multi level objects and does not use recursion.
var output = []
var o = {
x:0,
y:1,
z:{
x0:{
x1:4,
y1:5,
z1:6
},
y0:2,
z0:[0,1,2],
}
}
var defer = [ [ o ,[ '_root_' ] ] ]
var _defer = []
while(defer.length){
var current = defer.pop()
var root = current[1]
current = current[0]
for(var key in current ){
var path = root.slice()
path.push(key)
switch( current[key].toString() ){
case '[object Object]':
_defer.push( [ current[key] , path ] )
break;;
default:
output.push({
path : path ,
value : current[key]
})
break;;
}
}
if(!defer.length)
defer = _defer.splice(0,_defer.length)
}
[
{ path: [ '_root_', 'x' ], value: 0 },
{ path: [ '_root_', 'y' ], value: 1 },
{ path: [ '_root_', 'z', 'y0' ], value: 2 },
{ path: [ '_root_', 'z', 'z0' ], value: [ 0, 1, 2 ] },
{ path: [ '_root_', 'z', 'x0', 'x1' ], value: 4 },
{ path: [ '_root_', 'z', 'x0', 'y1' ], value: 5 },
{ path: [ '_root_', 'z', 'x0', 'z1' ], value: 6 }
]
const array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
const obj = Object.fromEntries(array.map(item => [item.key, item.value]));
console.log(obj);
I would say to use npm package flat.
works amazing for nested objects and arrays.
var flatten = require('flat')
flatten({
key1: {
keyA: 'valueI'
},
key2: {
keyB: 'valueII'
},
key3: { a: { b: { c: 2 } } }
})
// {
// 'key1.keyA': 'valueI',
// 'key2.keyB': 'valueII',
// 'key3.a.b.c': 2
// }
const array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
const obj = Object.fromEntries(array.map(item => [item.key, item.value]));
console.log(obj);
Having input like the below:
[
{
gameId: id_0,
groups: [1]
},
{
gameId: id_1,
groups: [2]
},
{
gameId: id_2,
groups: [1, 2]
},
{
gameId: id_3,
groups: [3]
}
]
I would like my reduce to result in an array of objects like:
[
{
group: 1,
data: [
id_0, id_2 // gameId
]
},
{
group: 2,
data: [
id_1, id_2
]
},
{
group: 3,
data: [
id_3
]
}
]
I was able to partially solve this by utilising array indexes.
The code I have currently is:
groupByArr = parameter => data => data.reduce((acc, curr) => {
curr[parameter].forEach(key => {
if (acc[key]) {
acc[key].push(curr)
} else {
acc[key] = [curr]
}
})
return acc
}, [])
which produces an array of arrays where main array index is the group id:
[
empty,
1: [
id_0, id_2
],
2: [
id_1, id_2
],
3: [
id_3
]
]
You can use Array.prototype.reduce() combined with Array.prototype.forEach() and Array.prototype.push() to return an Object and finally get the values with Object.values()
Code:
const data = [{gameId: 'id_0',groups: [1]},{gameId: 'id_1',groups: [2]},{gameId: 'id_2',groups: [1, 2]},{gameId: 'id_3',groups: [3]}]
const result = Object.values(data.reduce((acc, {gameId, groups}) => {
groups.forEach(group => {
acc[group] = acc[group] || { group, data: [] }
acc[group].data.push(gameId)
})
return acc
}, {}))
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Let arr be the variable containing data in following format:
[
empty,
1: [
id_0, id_2
],
2: [
id_1, id_2
],
3: [
id_3
]
]
Then following code might do:-
for(let i=1;i<arr.length;i++){
arr[i-1] = {group:i,data:arr[i]};
}
arr[arr.length-1] = undefined; // To Prevent Unnecessary Errors
delete arr[arr.length-1]; // To Prevent Unnecessary Errors
arr.length -= -1;
Now arr will be in following format:-
[
{
group: 1,
data: [
id_0, id_2
]
},
{
group: 2,
data: [
id_1, id_2
]
},
{
group: 3,
data: [
id_3
]
}
]
Hope it helps. Tell me if I Misunderstood your question.
Use an object as acc, then add objects to it:
if (acc[key]) {
acc[key].data.push(curr)
} else {
acc[key] = { group: key, data: [curr] };
}
And finally, turn the returned object into an array:
const result = Object.values(hash);
Try this
const data = [
{gameId: 'id_0',groups: [1]},
{gameId: 'id_1',groups: [2]},
{gameId: 'id_2',groups: [1, 2]},
{gameId: 'id_3',groups: [3]}
]
const single_id = data.filter(i => i.groups.length === 1)
const multiple_ids = data.filter(i => i.groups.length > 1)
let res = [];
single_id.map(i => {
let data = {group: i.groups[0], data: [i.gameId]}
multiple_ids.forEach(o => o.groups.includes(i.groups[0]) && (data.data.push(o.gameId)))
res.push(data)
})
console.log(res)
`let b = [
{
gameId: "id_0",
groups: [1]
},
{
gameId: "id_1",
groups: [2]
},
{
gameId: "id_2",
groups: [1, 2]
},
{
gameId: "id_3",
groups: [3]
}
];
let d = new Map(); ;
b.forEach(function(a){
a.groups.forEach(function(a1){
if(d.get(a1)){
d.get(a1).push(a.gameId);
}else{
d.set(a1, [a.gameId]);
}
});
});`
You should try this one. Using reduce in grouped scenarios is the best thing JavaScript provides us
let arr = [
{
gameId: "id_0",
groups: [1]
},
{
gameId: "id_1",
groups: [2]
},
{
gameId: "id_2",
groups: [1, 2]
},
{
gameId: "id_3",
groups: [3]
}
];
const grouped = arr.reduce((acc, current) => {
for(let x of current.groups){
if(!acc[x]) {
acc[x] = {group: x, data:[current.gameId]}
} else {
acc[x].data.push(current.gameId)
}
}
return acc
},{});
console.log(Object.values(grouped))
I'm trying to do a group by over an array of objects. The array that I'm trying to group by is subject to change and I need a solution that's dynamic.
This is how the array that i'm trying to work on looks like.
const arr = [
{
first: {
label: 'a',
key: 'a'
},
second: {
label: 'b',
key: 'b',
}
},
{
first: {
label: 'aa',
key: 'aa'
},
second: {
label: 'bb',
key: 'bb',
}
}
]
I've tried this so far:
const result = arr.reduce((acc, curr) => {
acc['first'] = acc['first'] || [];
acc['second'] = acc['second'] || [];
acc['first'].push(curr.first);
acc['second'].push(curr.second);
return acc;
}, {});
This solves my problem, but it's not a dynamic solution.
This is the expected result:
const obj = {
first: [
{
label: 'a',
key: 'a'
},
{
label: 'aa',
key: 'aa'
}
],
second: [
{
label: 'b',
key: 'b'
},
{
label: 'bb',
key: 'bb'
}
]
}
To make this more general, you simply need to loop over the keys, rather than hardcoding your first/second code. That should look something like this:
const result = arr.reduce((acc, curr) => {
let keys = Object.keys(curr);
keys.forEach((key) => {
acc[key] = acc[key] || [];
acc[key].push(curr[key]);
});
return acc;
}, {});
You can use reduce and Object.entries
const arr = [{first: {label: 'a',key: 'a'},second: {label: 'b',key: 'b',}},{first: {label: 'aa',key: 'aa'},second: {label: 'bb',key: 'bb',}}]
let final = arr.reduce((op, inp) => {
Object.entries(inp).forEach(([key, value]) => {
op[key] = op[key] || []
op[key].push(value)
})
return op
},{})
console.log(final)
I am trying to check if object array A includes objects from B.
let A = [
{ name: "Max" },
{ name: "Jhon" },
{ name: "Naton" },
]
let B = [
{ name: "Max" },
{ name: "Naton" },
]
So B has two objects that is in array A. How to check this ?
I am trying to achieve it with includes :
for(let entry of this.b){
if(this.a.includes(entry)){
console.log('includes');
}
}
But I get false on includes.
The method Array.includes() compare the entries of the array with the given value. Because your array entries are objects, it will not match. You have to loop at the array yourself and make the comparison.
Array.some() loops on an array and returns true if you returns true at least one. This method is useful when you want to verify something. In our example, we want to verify if the array a contains the b entry.
const a = [{
name: 'Max',
},
{
name: 'Jhon',
},
{
name: 'Naton',
},
];
const b = [{
name: 'Max',
},
{
name: 'Naton',
},
{
name: 'Daddy',
},
];
console.log(b.map(x => a.some(y => y.name === x.name)));
If I break it down :
const a = [{
name: 'Max',
},
{
name: 'Jhon',
},
{
name: 'Naton',
},
];
const b = [{
name: 'Max',
},
{
name: 'Naton',
},
{
name: 'Daddy',
},
];
// Loop on every entry of the b array
b.forEach((x) => {
// x here represent one entry
// first it will worth { name: 'Max' }, then { name: 'Naton' } ...
// for each value we are going to look at a if we can find a match
const isThereAMatch = a.some((y) => {
// y here is worth one entry of the a array
if (y.name === x.name) return true;
return false;
});
if (isThereAMatch === true) {
console.log(`We have found ${x.name} in a`);
} else {
console.log(`We have not found ${x.name} in a`);
}
});
You have to use another loop, then check the property name:
var a = [
{name: "Max"},
{name: "Jhon"},
{name: "Naton"},
];
var b = [
{name: "Max"},
{name: "Naton"},
];
for(let entry of b){
for(let entry2 of a){
if(entry2.name == entry.name){
console.log('includes', entry.name);
}
}
}
OR: You can use string version of object to check with includes():
var a = [
{name: "Max"},
{name: "Jhon"},
{name: "Naton"},
];
var b = [
{name: "Max"},
{name: "Naton"},
];
var aTemp = a.map(i => JSON.stringify(i));
var bTemp = b.map(i => JSON.stringify(i));
for(let entry of bTemp){
if(aTemp.includes(entry)){
console.log('includes', entry);
}
}
When you use Array#includes() method it will always return false because it's comparing objects which aren't equal because they aren't referencing the same object.
You should compare objects properties and not whole objects, you can do it using Array#some() method like this:
for (let entry of this.b) {
if (this.b.some(x => x.name === entry.name)) {
console.log('includes');
}
}
Demo:
A = [{
name: "Max"
},
{
name: "Jhon"
},
{
name: "Naton"
},
]
B = [{
name: "Max"
},
{
name: "Naton"
},
]
//Filter objects that exists in both arrays
let result = A.filter(el=> B.some(x => x.name === el.name));
console.log(result);