Create array of objects from an object using javascipt? - javascript

My initial data is coming like this..
let initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1 , 2 , 3]
};
I need output in this format..
FINAL_OUTPUT = [
{ labels: 'label1', children: 'child1', numbers: 1 },
{ labels: 'label2', children: 'child2', numbers: 2 },
{ labels: 'label3', children: 'child3', numbers: 3 }
];
Separated the keys and values from the initial object. But struck in creating array of objects from the same. Please help.

You could get the entries and map the inner array with the values at the given index.
let initial = { labels: ['label1', 'label2', 'label3'], children: ['child1', 'child2', 'child3'], numbers: [1, 2, 3] },
result = Object
.entries(initial)
.reduce((r, [k, a]) => a.map((v, i) => ({ ...(r[i] || {}), [k]: v })), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

This should do exactly what you want, assuming all arrays have equal length. With a bit of creativity this algorithm can be generalized to objects of arbitrary fields count. Here's the fiddle.
let initial = {
labels: [
'label1',
'label2',
'label3'
],
children: [
'child1',
'child2',
'child3'
],
numbers: [
1,
2,
3
]
};
function convertObjectToArray(
labels,
children,
numbers
) {
let final = [];
for (let i = 0; i < numbers.length; i++) {
final.push({
labels: labels[i],
children: children[i],
numbers: numbers[i],
});
}
return final;
}
console.log(convertObjectToArray(
initial.labels,
initial.children,
initial.numbers
));

I don't know of any built in javascript functions, but you can create a for loop (assuming that all arrays in the object are of same length):
for(let i = 0; i < initial[Object.keys(initial)[0]].length; i++){
FINAL_OUTPUT.push({
labels: initial.labels[i],
children: initial.children[i],
numbers: initial.numbers[i]
});
}

let initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1 , 2 , 3]
};
let keys = Object.keys(initial);
let keyLength = keys[0].length;
let sampleValueArray = initial[keys[0]];
let i = 0;
let result = sampleValueArray.map( (item,index) => {
let temp = {};
keys.forEach( key =>
temp[key] = initial[key][index]
)
return temp
})
console.log(result)

let initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1, 2, 3]
}
/* FINAL_OUTPUT = [
{ labels: 'label1', children: 'child1', numbers: 1 },
{ labels: 'label2', children: 'child2', numbers: 2 },
{ labels: 'label3', children: 'child3', numbers: 3 }
]; */
const result = Object.keys(initial).reduce((acc, x, i, keys) => {
const arr = keys.map((y, j) => initial[y][i]);
acc = [...acc, keys.reduce((acc_2, z, k) => ({
...acc_2,
[z]: arr[k]
}), [])]
return acc
}, [])
console.log(result)

let FINAL_OUTPUT =[]
for(let i =0;i<initial.length;i++){
FINAL_OUTPUT.push(
{labels: initial.labels[i],
children: initial.children[i],
numbers: initial.numbers[i]
})
}

You can use lodash's _.flow() to create a function that:
Uses to pairs to get an array of array of [key, [values]] pairs
Unzip to get separate keys from values
Destructure the keys and the values. Use _.unzip() to transpose the values, and then map, and use _.zipObject() with the keys to create the objects.
const { flow, toPairs, unzip, zipObject } = _;
const fn = flow(
toPairs, // convert to [key, values] pair
unzip, // transpose to array of keys, and array of values
([keys, values]) => // destructure keys and values
unzip(values) // transpose the values
.map(vals => zipObject(keys, vals)) // create object from keys and each set of values
);
const initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1, 2, 3]
};
const result = fn(initial);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Related

push item to same nested child in array deep

I can't be able to figure out how to push items to same nested array like :
var arr = ['foo', 'bar', 'buz', 'hello'];
// It should be now look like this:
const output = {name: 'foo', children: [
{name: 'bar', children: [
{name: 'buz', children: [
{name: 'hello', children: []}
]}
]}
]};
Using reduce:
const arr = ['foo', 'bar', 'buz', 'hello'];
const result = arr.reverse().reduce((acc, val) => ({name: val, children: [acc]}), {});
console.log(result);
You can use reduceRight to create the output.
var arr = ['foo', 'bar', 'buz', 'hello'],
result = arr.reduceRight((r, name) => ({name, children: (!Object.keys(r).length ? [] : [r])}), {});
console.log(result);
Using a recursive function:
const arr = ['foo', 'bar', 'buz', 'hello'];
const f = (arr) => ({name: arr.shift(), children: arr.length ? [f(arr)] : []});
const output = f(arr);
console.log(output);
Using a recursive function
function nested (arr) {
if (arr.length === 1)
return { name: arr.shift(), children: [] };
else if (arr.length > 1)
return { name: arr.shift(), children: [nested(arr)] };
}
var array = ['foo', 'bar', 'buz', 'hello']
console.log(nested(array))
I now have an idea on how Array.reduce() method work, so posted another answer without recursive approach. I think it would be better for large tree structure.
The basic idea is that simply reverse the input array and then wrap the innermost object into another and so on.
let arr = ['foo', 'bar', 'buz', 'hello'];
function nest(arr) {
arr = arr.reverse();
let out = [];
arr.forEach(it => {
if(out.length === 0) out = {name: it, children: []}
else {
out = {name: it, children: [out]}
}
});
return out;
}

relative complement of A in B with functional programming

I have to retrieve the values that exist only on Array B, but do not exist on Array A.
From my research, It is called:
relative complement of A in B
Values in the arrays may not be primitives.I need an efficient and functional apporach to this problem.
I have found lodash _.without function, but it supports only array of primitive numbers.
Array A:
[{
id: 1
},
{
id:2
}]
Array B:
[{
id:2
},
{
id:3
}]
result should be:
[{
id:3
}]
this object is the only one who exist on Array B, but not on Array A.
You could use a comparison function which takes two objects and check the id for unequalness.
var aa = [{ id: 1 }, { id: 2 }],
bb = [{ id: 2 }, { id: 3 }],
comparison = (a, b) => a.id !== b.id,
result = bb.filter(b => aa.every(a => comparison(a, b)));
console.log(result);
With a check for equalness
var aa = [{ id: 1 }, { id: 2 }],
bb = [{ id: 2 }, { id: 3 }],
comparison = (a, b) => a.id === b.id,
result = bb.filter(b => aa.every(a => !comparison(a, b)));
console.log(result);
You can use array#filter with array#some. Iterate through arrB and check if the arrA contains that id using array#some and negate the result of array#some.
var arrA = [{id: 1},{id:2}],
arrB = [{id:2},{id:3}],
result = arrB.filter(({id}) => !arrA.some(o => o.id === id));
console.log(result);
You can use array.prototype.filter and array.prototype.findIndex:
var arrayA = [{ id: 1 }, { id: 2 }];
var arrayB = [{ id: 2 }, { id: 3 }];
var result = arrayB.filter(b => arrayA.findIndex(a => a.id === b.id) === -1);
console.log(result);
If you want to use lodash, _.differenceBy could be of use:
relativeComplementOfAinB = _.differenceBy(arrayB, arrayA, v => v.id);

Javascript array de-structuring and Object.entries

It's pretty simple: Object.entries is supposed to produce and Array of key, value pairs.. As such, I would expected this code to destructure
[{
id: 1,
name: "christian"
},{
id : 2,
name: "bongiorno"
}].map(Object.entries).forEach(([k,v]) => console.log(`${k}: ${v}`));
into producing:
id:1 name:christian
id:2 name:bongiorno
But it doesn't. I get, instead:
id,1: name,christian
id,2: name,bongiorno
What did I miss?
The output is correct but your definition is slightly off, you're missing an array level (array of arrays).
Object.entries is supposed to produce an array of arrays of key, value pairs.
console.log(
Object.entries({
id: 1,
name: 'test'
})
)
To achieve what you want, you can just update your log to account for nested arrays:
[{
id: 1,
name: "christian"
},{
id : 2,
name: "bongiorno"
}]
.map(Object.entries)
.forEach(([k, v]) => console.log(
`${k.join(':')} ${v.join(':')}`
));
Or maybe you meant to flatten each array?:
[{
id: 1,
name: "christian"
},{
id : 2,
name: "bongiorno"
}]
.map(Object.entries)
.reduce((arr, curr) => arr.concat(curr), [])
.forEach(([k,v]) => console.log(`${k}: ${v}`));
Let's try to drop the map and forEach and see what you did with each of the objects:
let [k, v] = Object.entries({
id: 1,
name: "christian"
});
console.log(`${k}: ${v}`);
let [k, v] = Object.entries({
id : 2,
name: "bongiorno"
});
console.log(`${k}: ${v}`);
Now if we expand the Object.entries call, this becomes
let [k, v] = [
["id", 1],
["name", "christian"]
];
console.log(`${k}: ${v}`);
let [k, v] = [
["id", 2],
["name", "bongiorno"]
];
console.log(`${k}: ${v}`);
which quite accurately reflects what you're seeing - k and v are getting assigned arrays.
You will need to nest two loops:
const arr = [{
id: 1,
name: "christian"
}, {
id: 2,
name: "bongiorno"
}];
for (const obj of arr)
for (const [k, v] of Object.entries(obj))
console.log(k+": "+v);

Sort array of object based on another array

So let's say I have an array:
const chunks = [
{id: 0, names: ['app']},
{id: 1, names: ['contact']},
{id: 2, names: ['bootstrap']}
];
And I want it to be sorted based on the names property, so the order is like in this array:
const original = ['bootstrap', 'app', 'contact'];
What is the most efficient way to do this?
You could use the delta of the indices of names in original.
const chunks = [{ id: 0, names: ['app'] }, { id: 1, names: ['contact'] }, { id: 2, names: ['bootstrap'] }],
original = ['bootstrap', 'app', 'contact'];
chunks.sort((a, b) => original.indexOf(a.names[0]) - original.indexOf(b.names[0]));
console.log(chunks);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try this method:
const chunks = [{id: 0, names: ['app']}, {id: 1, names: ['contact']}, {id: 2, names: ['bootstrap']}];
const original = ['bootstrap', 'app', 'contact'];
let result = [];
for(let i = 0; i < original.length; i++) {
for(let j = 0; j < chunks.length; j++) {
if(chunks[j].names.indexOf(original[i]) !== -1) {
result.push(chunks[j]);
}
}
}
console.log(result);
Easy way: convert chunks into an object so you get the correct one with just the key, then map over the (already sorted) array to plug in the object in place.
cMap = chunks.reduce((p,c) => Object.assign( p, {[c.names[0]]: c} ), {});
const sorted = original.map(k => cMap[k]);

Combine object and arrays

I'm trying to write a function that takes an array of objects, and an unlimited number of arrays, and combines them to form a single object. The inputs would follow this pattern:
let x = [{ name: 'Tom' }, { name: 'John' }, { name: 'Harry' }];
let y = [[1, 2, 3], 'id'];
let z = [['a', 'b', 'c'], 'value'];
combine(x, y, z);
With the second element of y and z acting as the object key. Using these arguments, the function should return the following array:
[
{
name: 'Tom',
id: 1,
value: 'a'
},
{
name: 'John',
id: 2,
value: 'b'
},
{
name: 'Harry',
id: 3,
value: 'c'
},
]
The index of the current object should be used to get the correct element in the array. I have made an attempt at the problem:
function combine(object, ...arrays) {
return object.map((obj, index) => {
let items = arrays.map(arr => ({
[arr[1]]: arr[0][index]
}));
return Object.assign({}, obj, { items });
});
}
This almost does the job, but results in the array items being hidden inside a nested items array, How can I solve this?
You had been assigning an object of object, and the result was a new object with the element items inside (another feature of object literal).
This approach use reduce instead of map and direct assign instead of object literal.
function combine(object, ...arrays) {
return object.map((obj, index) => {
const items = arrays.reduce((acc, arr) => {
acc[arr[1]] = arr[0][index] ;
return acc;
}, {});
return Object.assign({}, obj, items);
});
}
const x = [{ name: 'Tom' }, { name: 'John' }, { name: 'Harry' }];
const y = [[1, 2, 3], 'id'];
const z = [['a', 'b', 'c'], 'value'];
combine(x, y, z);
You can also use the spread operator in the Object.assign, like this:
function combine(object, ...arrays) {
return object.map((obj, index) => {
let items = arrays.map(arr => ({
[arr[1]]: arr[0][index]
}));
return Object.assign({}, obj, ...items);
});
}
This almost does the job, but results in the array items being hidden inside a nested items array
The problem is that items is an array, whereas you only need the current item inside of that particular map callback. No need to nest loops here.
Also I would recommend avoiding multiple properties per combine call. The resulting code would look like this:
function combine(objects, [values, key]) {
return objects.map((o, i) =>
Object.assign({[key]: values[i]}, o)
);
}
combine(combine(x, y), z);
If you then have multiple extensions to do, you can also use
[y, z].reduce(combine, x)
With map and computed keys, you can achieve this.
Here's a working example:
let x = [{
name: 'Tom'
}, {
name: 'John'
}, {
name: 'Harry'
}];
let y = [[1, 2, 3], 'id'];
let z = [['a', 'b', 'c'], 'value'];
let result = [];
x.map(function (el, index) {
result.push(el);
let index = result.length -1;
result[index][y[1]] = y[0][index];
result[index][z[1]] = z[0][index];
});
console.log(result);

Categories

Resources