Javascript array de-structuring and Object.entries - javascript

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

Related

How to Group Element in Javascript to get the Cartesian product in javascript

I have a scenario where I have an object just like this.
[
{attributeGroupId:2, attributeId: 11, name: 'Diamond'}
{attributeGroupId:1, attributeId: 9, name: '916'}
{attributeGroupId:1, attributeId: 1, name: '24K'}
{attributeGroupId:2, attributeId: 12, name: 'Square'}
]
Expected result:
[
{attributeGroupId:2, attributeId: 11, name: 'Diamond'},
{attributeGroupId:2, attributeId: 12, name: 'Square'}
]
,
[
{attributeGroupId:1, attributeId: 9, name: '916'},
{attributeGroupId:1, attributeId: 1, name: '24K'}
]
So I can make the cartesian product of it just like this,
[
{attributeId: 11-9, name: 'Diamond-916'},
{attributeId: 11-1, name: 'Diamond-24K'},
{attributeId: 12-9, name: 'Square-916'},
{attributeId: 12-1, name: 'Square-24K'},
]
Now I want to keep this logic as generic as possible as the number of attributeGroupId is not known during runtime.
I think that splitting the array into multiple smaller array on the basis of attributeGroupId should be the first step.
You can implement a basic filter function like this:
function filterForAttributeGroupId(data, id) {
return data.filter((item) => item.attributeGroupId === id);
}
console.log(filterForAttributeGroupId(data, 1)) //contains all elements with attributeGroupId = 1
console.log(filterForAttributeGroupId(data, 2)) //contains all elements with attributeGroupId = 2
Here you have a generic solution returning an array of filtered Arrays:
function filterForAttributeGroupId(data) {
const mem = {};
data.forEach((item) => {
if ( mem[item.attributeGroupId] ) {
mem[item.attributeGroupId].push(item);
} else {
mem[item.attributeGroupId] = [item];
}
})
return Object.values(mem);
}
Edit after feedback from comment
If the order of concatenated attributes does not matter, you can use the following code to get the "cartesian concatenation" of n different arrays:
function cartesianProduct(arrays) {
if (arrays.length <= 1 ) return arrays[0];
const first = arrays[0];
const second = cartesianProduct(arrays.slice(1));
const result = [];
first.forEach(( itemFirst ) => {
second.forEach( (itemSecond) => {
result.push({attributeId: `${itemFirst.attributeId}-${itemSecond.attributeId}`, name: `${itemFirst.name}-${itemSecond.name}`})
});
});
return result;
}
This way calling the following:
console.log(cartesianProduct(filterForAttributeGroupId(data)));
results in the expected data (although the strings are concatenated in another order):
[
{
"attributeId":"9-11",
"name":"916-Diamond"
},
{
"attributeId":"9-12",
"name":"916-Square"
},
{
"attributeId":"1-11",
"name":"24K-Diamond"
},
{
"attributeId":"1-12",
"name":"24K-Square"
}
]
I think it's better to break this logic into pieces. First, a cartesian product function for an array of arrays seems very useful. So let's make a utility function for that. Second, breaking an array into subarrays based on some shared feature is also common. So let's make a second one for that.
Then we can write a simple main function which groups the id by attributeGroupId, and calls the cartesian product on that, then for each item in the resulting array like
[
{attributeGroupId: 2, attributeId: 11, name: "Diamond"},
{attributeGroupId: 1, attributeId: 9, name: "916"}
]
We can create a new object by combining the attributeId and name properties
const cartesian = ([xs, ...xss]) =>
xs == undefined ? [[]] : xs .flatMap (x => cartesian (xss) .map (ys => [x, ...ys]))
const group = (fn, k) => (xs) => Object .values (xs .reduce (
(a, x) => ((k = 'x' + fn (x)), (a [k] = a [k] || []), (a [k] .push (x)), a), {}
))
const regroupAtts = (xs) =>
cartesian (group (x => x .attributeGroupId) (xs))
.map (xs => ({
attributeId: xs .map (x => x .attributeId) .join ('-'),
name: xs .map (x => x.name) .join ('-')
}))
const atts = [{attributeGroupId: 2, attributeId: 11, name: 'Diamond'}, {attributeGroupId: 1, attributeId: 9, name: '916'}, {attributeGroupId: 1, attributeId: 1, name: '24K'}, {attributeGroupId: 2, attributeId: 12, name: 'Square'}]
const atts2 = [{attributeGroupId: 2, attributeId: 11, name: 'Diamond'}, {attributeGroupId: 1, attributeId: 9, name: '916'}, {attributeGroupId: 3, attributeId: 101, name: 'foo'}, {attributeGroupId: 1, attributeId: 1, name: '24K'}, {attributeGroupId: 3, attributeId: 102, name: 'bar'}, {attributeGroupId: 2, attributeId: 12, name: 'Square'}, {attributeGroupId: 3, attributeId: 103, name: 'baz'}]
console .log ('Original:', regroupAtts (atts))
console .log ('With third group added:', regroupAtts (atts2))
.as-console-wrapper {max-height: 100% !important; top: 0}
We add a third attribute with entries of name "foo", "bar", and "baz" to show how this extends to more attributes. Of course, you'll have to consider combinatorial explosions if you add too many such attributes, but the code is designed to handle it.
And interesting abstraction from here is if you wanted to parameterize the names "attributeGroupId", "attributeId", and "name". We might choose to do this:
const regroup = (key, props) => (xs) =>
cartesian (group (x => x [key]) (xs))
.map (xs => Object .fromEntries (
props .map (prop => [prop, xs .map (x => x [prop]) .join ('-')])
))
const regroupAtts = regroup ('attributeGroupId', ['attributeId', 'name'])
const cartesian = ([xs, ...xss]) =>
xs == undefined ? [[]] : xs .flatMap (x => cartesian (xss) .map (ys => [x, ...ys]))
const group = (fn, k) => (xs) => Object .values (xs .reduce (
(a, x) => ((k = 'x' + fn (x)), (a [k] = a[k] || []), (a[k] .push (x)), a), {}
))
const regroup = (key, props) => (xs) =>
cartesian (group (x => x [key]) (xs))
.map (xs => Object .fromEntries (
props .map (prop => [prop, xs .map (x => x [prop]) .join ('-')])
))
const regroupAtts = regroup ('attributeGroupId', ['attributeId', 'name'])
const atts = [{attributeGroupId: 2, attributeId: 11, name: 'Diamond'}, {attributeGroupId: 1, attributeId: 9, name: '916'}, {attributeGroupId: 1, attributeId: 1, name: '24K'}, {attributeGroupId: 2, attributeId: 12, name: 'Square'}]
const atts2 = [{attributeGroupId: 2, attributeId: 11, name: 'Diamond'}, {attributeGroupId: 1, attributeId: 9, name: '916'}, {attributeGroupId: 3, attributeId: 101, name: 'foo'}, {attributeGroupId: 1, attributeId: 1, name: '24K'}, {attributeGroupId: 3, attributeId: 102, name: 'bar'}, {attributeGroupId: 2, attributeId: 12, name: 'Square'}, {attributeGroupId: 3, attributeId: 103, name: 'baz'}]
console .log ('Original:', regroupAtts (atts))
console .log ('With third group added:', regroupAtts (atts2))
.as-console-wrapper {max-height: 100% !important; top: 0}
In writing it this way, we now have reusable cartesian and group functions, which appear often. In fact, I stole those from previous answers I've written. (I did alter group a bit by adding the 'x' + to the key generation. This is because your keys (attributeGroupIds) are simple numbers. When Object.values iterates an object like that, it does so by doing those first, in numeric order, then iterates the remaining String keys in insertion order, then the Symbols. By prepending a string, I make this all into insertion order, and get back the order you preferred. I'm trying to figure out if this variant of the function replaces the one in my usual toolbox.)

Create array of objects from an object using javascipt?

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>

Replacing the id in the array with the name from the second array

I have some problem with connecting two arrays with objects by id and replace that id by NAME from second array. For example I have arrays:
array1 = [
{id: [1, 2], info: "xxx"},
{id: [2, 3], info: "yyy"}
]
array2 = [
{nameId: 1, name: "Miami"},
{nameId: 2, name: "Wacanda"},
{nameId: 3, name: "London"},
]
And I want to modify array1 or create new array3 like (with use ES6):
array1 = [
{id: ["Miami", "Wacanda"], info: "xxx"},
{id: ["Wacanda", "London"], info: "yyy"}
]
or
array3 = [
{name: ["Miami", "Wacanda"], info: "xxx"},
{name: ["Wacanda", "London"], info: "yyy"}
]
Link to fast editing:
https://stackblitz.com/edit/angular-lrnfrd?file=src%2Fapp%2Ftesting%2Ftesting.component.ts
One possible solution is to first generate a Map between the ids and the names from the array2 using Array.reduce(). Then you can use Array.map() over the array1 to get the desired data. Note in the next example I have choice your second sample of output.
const array1 = [
{id: [1, 2], info: "xxx"},
{id: [2, 3], info: "yyy"}
];
const array2 = [
{nameId: 1, name: "Miami"},
{nameId: 2, name: "Wacanda"},
{nameId: 3, name: "London"},
];
let nameFromId = array2.reduce(
(acc, {nameId, name}) => (acc.set(nameId, name), acc),
new Map()
);
let res = array1.map(({id, info}) => ({name: id.map(i => nameFromId.get(i)), info}));
console.log(res);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
Another alternative without generating a Map is to use Array.find() inside the map() function:
let res = array1.map(
({id, info}) => ({name: id.map(i => array2.find(o => o.nameId === i).name), info})
);
Really simple - just use nested map with find:
const array1 = [{id:[1,2],info:"xxx"},{id:[2,3],info:"yyy"}];
const array2 = [{nameId:1,name:"Miami"},{nameId:2,name:"Wacanda"},{nameId:3,name:"London"}];
const array3 = array1.map(({ id, info }) => ({ name: id.map(e => array2.find(({ nameId }) => nameId == e).name), info }));
console.log(array3);
.as-console-wrapper { max-height: 100% !important; top: auto; }
This will generate your second example:
var array3 = array1.map(({ id, info }) => ({
name: array2.filter(({ nameId }) => id.includes(nameId)).map(({ name }) => name),
info
}));
First it filters on the nameId being in the list of ids from array1, then it runs map to just return the name. The expected output becomes:
[
{"name":["Miami","Wacanda"],"info":"xxx"},
{"name":["Wacanda","London"],"info":"yyy"}
]

Merge array into array of objects

I'm working with API data and I'm trying to build an object combining multiple arrays of data.
Current Arrays:
let name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
let arr1 = ['bar', 'foo', 'foobar']
let arrX = ...
Desired Outcome:
let desiredOutcome = [
{
name: "John",
arr1: "bar", ...
},
{
name: "Jane",
arr1: "foo", ...
},
{
name: "Doe",
arr1: "foobar", ...
}]
I've been trying to play around with Object.assign() but I haven't had any luck:
var merge = Object.assign(obj, arr1 )
Is there a method or methods I could use?
Use .map() to add each element.
let name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
let arr1 = ['bar', 'foo', 'foobar']
let result = name.map((a,i)=>{a.arr1 = arr1[i]; return a})
console.log(result)
You can do it using Array.map
Try the following:
let name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
let arr1 = ['bar', 'foo', 'foobar'];
var result = name.map((o,i) =>Object.assign({"arr1" : arr1[i]},o));
console.log(result);
For an arbitrary count of arrays, you could take an array with the array of objects and the arrays of values and take short hand properties which preserves the name of the array and the values for adding to the result set with Object.assign.
var names = [{ name: "John" }, { name: "Jane" }, { name: "Doe" }],
arr1 = ['bar', 'foo', 'foobar'],
arrX = [1, 2, 3],
result = [names, { arr1 }, { arrX }]
.reduce((r, o) =>
(([k, a]) => a.map((v, i) => Object.assign({}, r[i], { [k]: v })))(Object.entries(o)[0])
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Maybe late but I'll provide some additional explanation on how to use .map:
The .map method creates a new array with the results of calling a provided function on every element in the calling array.
The method takes in one callback as argument. The callback itself can take predefined arguments. Here we will use the first two:
currentValue: e
index: i
Basically, the map method works by associating (literally mapping) each element of the looped array (here name) to the given returned value (here {name: e.name, arr1: arr1[i]}). Mapping is just a bijection between two arrays.
Another word on (e,i) => ({name: e.name, arr1: arr1[i]}):
It is the shorthand syntax called arrow function. It is similar to defining the callback function like so:
function(e,i) {
return { name: e.name, arr1: arr1[i] };
}
Full snippet will look like:
const name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
const arr1 = ['bar', 'foo', 'foobar']
const result = name.map((e,i) => ({ name: e.name, arr1: arr1[i] }))
console.log(result)

How to extract property of array in nested array

I have an array, which contains array of objects. I need to extract the property value "id" of items that have objects.
Example of array:
let myArray = [
[ {id: "1"}, {id: "2"} ],
[],
[],
[ {id: "3"} ]
]
How can I extract and create an array like this:
["1", "2", "3"]
I tried this:
tagIds = myArray.map(id =>{id})
You can use reduce to flatten the array and use map to loop thru the array and return the id.
let myArray = [
[{id: "1"}, {id: "2"}],
[],
[],
[{id: "3"}],
];
let result = myArray.reduce((c, v) => c.concat(v), []).map(o => o.id);
console.log(result);
Another way with simple nested loops:
let myArray = [
[ {id: "1"}, {id: "2"} ],
[],
[],
[ {id: "3"} ]
]
//----------------------------------
let newArray=[];
for (let i=0;i<myArray.length;i++){
for (let j=0;j<myArray[i].length;j++){
newArray.push(myArray[i][j].id);
}
}
console.log(newArray); //outputs ['1','2','3']
You can use .concat() to create array of single objects and then .map() to extract ids:
let myArray = [
[{id: "1"}, {id: "2"}], [], [], [{id:"3"}]
];
let result = [].concat(...myArray).map(({ id }) => id);
console.log(result);
Docs:
Array.prototype.concat()
Array.prototype.map()
Spread Syntax
Here is my solution:
let a = myArray.flat(100) // you can put (3) or (10) in here, the higher the flatter the array
let b = a.map(
function(value){
return parseInt(value.id)
}
)
console.log(b)
You can also write a recursive function to make this work with any number of arrays, for example:
function extractIds(arr) {
return arr.reduce((a, item) => {
if (Array.isArray(item)) {
return [
...a,
...extractIds(item)
];
}
return [
...a,
item.id
];
}, [])
}
extractIds([{id: 1}, [{id: 2}], {id: 3}, [{id: 4}, [{id: 5}, [{id: 6}]]]])
the return of extractIds will be [1, 2, 3, 4, 5, 6].
Notice that without the recursive part you would end up with something like this: [1, 2, [3, {id: 4}, [{id: 5}]]] (Not exactly like this, but just as an example).

Categories

Resources