Flatten arrays in an object - javascript

I have an object as below:
const USER_MAP = {
PREMIUM: ['a', 'b'],
RETAIL: ['c', 'd']
};
I would like to transform to below
[
{ segment: 'PREMIUM', id: 'a' },
{ segment: 'PREMIUM', id: 'b' },
{ segment: 'RETAIL', id: 'c' },
{ segment: 'RETAIL', id: 'd' }
]
I came up with a solution as below
const USER_MAP = {
PREMIUM: ['a', 'b'],
RETAIL: ['c', 'd']
};
const userList = Object.entries(USER_MAP).reduce((accumulator, currentValue) => {
const userListByType = currentValue[1].map(id => ({ id, segment: currentValue[0]}))
return [...accumulator, ...userListByType]
}, []);
console.log(userList);
It works but im wondering if there might be a better way to achieve above? In terms of readability as I'm nesting a map in a reduce, it seems to me that I might've complicated stuffs here

You could take Array#flatMap with a mapping of nested objects with outer key.
const
USER_MAP = { PREMIUM: ['a', 'b'], RETAIL: ['c', 'd'] },
result = Object
.entries(USER_MAP)
.flatMap(([segment, a]) => a.map(id => ({ segment, id })));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Expanding arrays in object properties to an array of objects

How am I able to convert the following object:
data = {
sp: [ 'A', 'B', 'C' ],
jh: [ '1', '0', 'AB' ],
oa: [ 27493, 9837, 3781 ]
}
into the following array of objects:
new_data = [
{sp: 'A', jh: '1', oa: 27493},
{sp: 'B', jh: '0', oa: 9837},
{sp: 'C', jh: 'AB', oa: 3781}
]
Assuming the arrays are all the same size, map one of them and reduce the keys using the current index.
const data = {
sp: [ 'A', 'B', 'C' ],
jh: [ '1', '0', 'AB' ],
oa: [ 27493, 9837, 3781 ]
};
let keys = Object.keys(data);
let new_data = data[keys[0]].map(( _, idx ) => keys.reduce((a, c) => ({ ...a, [c]: data[c][idx] }), {}));
console.log(new_data)
.as-console-wrapper { max-height: 100% !important; top: auto; }
You can use Object.entries to get all of the key-value pairs in the object.
You can take the length of the values of the array for the first entry as the length of the output, assuming that all of the arrays in the object are of the same length.
Then, you can map over all of the possible indexes and use Object.fromEntries to create an object with the keys from the original object and the value being the value at that index in the array for that key in the original object.
const data = {
sp: [ 'A', 'B', 'C' ],
jh: [ '1', '0', 'AB' ],
oa: [ 27493, 9837, 3781 ]
}
let entries = Object.entries(data);
let res = entries[0][1].map((_, i)=>Object.fromEntries(
entries.map(([k, v])=>[k, v[i]])));
console.log(res);

How to form This JSON from this Javascript array of arrays?

I have this JavaScript array of arrays
arr1= [
['a', 'b'],
['1', '2']
]
And I need to form this JSON object
[
{
"label":"a",
"value":"1"
},
{
"label":"b",
"value":"2"
},
]
How do I do it?
Using Array#map:
const arr = [ ['a', 'b'], ['1', '2'] ];
const [labels, values] = arr;
const res = labels.map((label, index) => ({ label, value: values[index] }));
console.log(res);
Look at this ...
const entries = new Map([
['foo', 'bar'],
['baz', 42]
]);
const obj = Object.fromEntries(entries);
console.log(obj);
// expected output: Object { foo: "bar", baz: 42 }
Did you mean something like this?
Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries
You could take an array with the keys of the wanted objects and reduce and map the items.
const
data = [['a', 'b'], ['1', '2']],
keys = ['label', 'id'],
result = data.reduce((r, a, i) => a.map((v, j) => ({ ...r[j], [keys[i]]: v })), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

lodash - filter collection by nested items count

Is there any simple solution (vanilla js or lodash) to filter collection by nested items count?
For example there is following grouped collection:
[
{
Items: ['a', 'b', 'c'],
Name: 'Group 1'
},
{
Items: ['d', 'e','f'],
Name: 'Group 2'
}
]
If I need to take 2 items it should return:
[
{
Items: ['a', 'b'],
Name: 'Group 1'
}
]
If I need to take 5 items it should return:
[
{
Items: ['a', 'b', 'c'],
Name: 'Group 1'
},
{
Items: ['d', 'e'],
Name: 'Group 2'
}
]
You need to iterate the items (for...of in this case), and count the number of items in the result + the current object items length.
If it's less or equal to the total wanted (n) you push the original object. If it's more, you slice the nested array, so it will include the difference.
If the current count is equal or more than n (or if the loop ends) return the result (res).
const fn = (arr, n, key) => {
let count = 0
const res = []
for(const o of arr) {
const len = o[key].length
res.push(count + len <= n ? o : { ...o, [key]: o[key].slice(0, n - count) })
count += len
if(count >= n) return res
}
return res
}
const arr = [{"Items":["a","b","c"],"Name":"Group 1"},{"Items":["d","e","f"],"Name":"Group 2"}]
console.log(fn(arr, 2, 'Items'))
console.log(fn(arr, 5, 'Items'))
console.log(fn(arr, 8, 'Items'))
My solution, maybe not perfect but it works :)
let array = [
{
Items: [
'a',
'b',
'c'
],
Name: 'Test1'
},
{
Items: [
'd',
'e',
'f'
],
Name: 'Test2'
}
];
let itemsCount = 5;
let filteredArray = [];
array.some(group => {
if (itemsCount <= 0) {
return true;
}
if (group.Items.length <= itemsCount) {
itemsCount -= group.Items.length;
} else {
group.Items = group.Items.slice(0, itemsCount);
itemsCount = 0;
}
filteredArray.push(group);
});
console.log(filteredArray);

Filtering an array with an array of values

I have 2 arrays and i'd like to filter one array with the other. E.g. if array1 includes any of the values in array2, they should be returned.
The two arrays are:
const array1 = [a, b, c, d]
The other array, which should be filtered where 'id' is equal to any of the values in array1 is:
const array2 = [
{
id: b
title: title1
},
{
id: d
title: title2
},
{
id: f
title: title3
}
]
The easiest way is to use two for-loops. Possible not the fastest approach.
res = [];
for (var i = 0;i<array1.length;i++) {
for (var j = 0;j<array2.length;j++) {
if (array1[i] == array2[j].id) {
res.push(array2[j]);
break;
}
}
}
You could use Array.prototype.filter() and Array.prototype.indexOf():
const array1 = ['a', 'b', 'c', 'd'];
const array2 = [{
id: 'b',
title: 'title1'
}, {
id: 'd',
title: 'title2'
}, {
id: 'f',
title: 'title3'
}];
const result = array2.filter(function(x){
return array1.indexOf(x.id) !== -1;
});
Adding this missing '', You can use filter and includes methods of Array.
const array1 = ['a', 'b', 'c', 'd'];
const array2 = [
{
id: 'b',
title: 'title1'
},
{
id: 'd',
title: 'title2'
},
{
id: 'f',
title: 'title3'
}
]
const result = array2.filter(({id}) => array1.includes(id));
console.log(result);

insert value in middle of every value inside array

I have a array has a value of ['a', 'b', 'c', 'd', 'e'] now I want it to become a object that have it's value so I do some array mapping
const arrs = ['a', 'b', 'c', 'd', 'e']
let arrObj = arrs.map(arr => {
return {value: arr}
})
Now the value of arrObj is
[{value: 'a'}, {value: 'b'}, {value: 'c'}, {value: 'd'}, {value: 'e'}]
But what I want to do is to insert a object in the middle of each object that is inside the array that has a value of {operator: '+'} so the value of arrObj will be
[{value: 'a'}, {operator: '+'}, {value: 'b'}, {operator: '+'}, {value: 'c'}, {operator: '+'}, {value: 'd'}, {operator: '+'}, {value: 'e'}]
now, using javascript, how can I achive that function given that I'm setting a value of arrObj in array.map() ?
You could create a new array with a double length minus one and add the required values, depending on the index.
var values = ['a', 'b', 'c', 'd', 'e'],
result = Array.from(
{ length: values.length * 2 - 1 },
(_, i) => i % 2
? { operator: '+' }
: { value: values[i >> 1] }
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
One option is to map each element (but the last) to an array with that element and another with the +, then flatten:
const arrs = ['a', 'b', 'c', 'd', 'e'];
const transformed = arrs
.map((char, i) => (
i === arrs.length - 1
? [{ value: char }]
: [{ value: char }, { value: '+' }]
))
.flat();
console.log(transformed);
If + won't appear in the input array, then you can join by + initially, then split:
const arrs = ['a', 'b', 'c', 'd', 'e'];
const output = arrs
.join('+')
.split('')
.map(value => ({ value }));
console.log(output);
Use flatMap and return a pair from the callback. Finally, remove the extra element.
const arrs = ['a', 'b', 'c', 'd', 'e']
let arrObj = arrs.flatMap(x => [
{operator: '+'}, {value: x}
]).slice(1)
console.log(arrObj)
If your platform doesn't have flatMap yet, it's trivial to polyfill:
Array.prototype.flatMap = function(fn) {
return this.concat.apply([], this.map(fn))
}
Generic function:
let interleave = (ary, val) => ary.flatMap(x => [val, x]).slice(1);
//
let arrObj = interleave(
arrs.map(x => ( {value: x})),
{operator: '+'}
)
What about using reduce?
let arrObj = arrs.reduce((acc, curr, index) => {
if (index === arrs.length - 1) acc = [...acc, {value: curr}];
else acc = [...acc, {value: curr}, {operator: '+'}];
return acc;
}, [])
Concat your required object and deep flatten it like below:
var arrs = ['a', 'b', 'c', 'd','e'];
const arrObj = arrs.map((arr,i) => {
let item = [{value: arr}];
if(i < arrs.length-1)
item.push({operator: '+'});
return item;
});
console.log(flattenDeep(arrObj));
function flattenDeep(arr1) {
return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
}
For more details take a look here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat

Categories

Resources