Flatten whilst splitting into smaller objects - javascript

I am having these kind of array structure.
[{ "primary_product": "Blueberry",
"list_of_products": ["Raspberry","Strawberry","Blackberry"]}]
I want to destructure the pattern and make it like this below
[{"id": 1,"value":"Blueberry"},{"id": 2,"value":"Raspberry"},{"id": 3,"value":"Strawberry"}, …]
Primary product will be the first product and then make the array of strings into key/value pair. How to do this using es6?

All you need is basic functions like forEach and push. I would recommend learning these.
let arr1 = [{ "primary_product": "Blueberry", "list_of_products": ["Raspberry","Strawberry","Blackberry"]}]
arr2 = [{ id: 1, value: arr1[0].primary_product }]
arr1[0].list_of_products.forEach((element) => {
arr2.push({ id: arr2.length + 1, value: element })
})

Here's a one-liner using map on the list_of_products:
const arr = ['Raspberry','Strawberry','Blackberry'];
return arr.map((val, i) => {return {id: i+1, value: val}});
This is the result:
[
{ id: 1, value: 'Raspberry' },
{ id: 2, value: 'Strawberry' },
{ id: 3, value: 'Blackberry' }
]
Note that the callback to map includes (currentValue, index, arr).

To make things slightly easier for the eyes I've simplified the structure:
const p = [ { a: 100, b: [101, 102, 103]}
, { a: 200, b: [201, 202, 203]}];
You can flatten all the numbers into a single list i.e. [100, 101, 102, 103, 200, 201, 202, 203] with:
p.flatMap(({a, b}) => [a, ...b]);
To get closer to what you're after, let's first create an id function that will return the next number:
const id = (n => () => ++n)(0);
id(); //=> 1
id(); //=> 2
id(); //=> 3
// …
Then let's create a function obj that takes an x and wraps it into an object:
const obj => x => ({id: id(), value: x});
obj(100); //=> {id: 1, value: 100);
obj(200); //=> {id: 2, value: 200);
// …
Then you can do:
p.flatMap(({a, b}) => [obj(a), ...b.map(obj)]);
//=> [ {id: 1, value: 100}
//=> , {id: 2, value: 101}
//=> , {id: 3, value: 102}
//=> , {id: 4, value: 103}
//=> , {id: 5, value: 200}
//=> , {id: 6, value: 201}
//=> , {id: 7, value: 202}
//=> , {id: 8, value: 203}]

Related

Filtering using two array of objects using JavaScript

I have an array as shown:
var arrOne = [{id: 3},{id: 8},{id: 12}];
And another array as shown:
var arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
I want to iterate arrTwo based on arrOne, and get the val values out of arrTwo.
So the result should be:
var result = ['Bailey', 'cathy', 'David'];
Tried concatenating .map with .filter:
arrOne.map(arOne => arrTwo.filter(artwo => {
if(arOne.id === artwo.id) {
return artwo.val
} else {
return false;
}
}));
But it gives me all, and where it is false it adds false there, which I don't want.
Any ideas where I am going wrong will be appreciated.
Editing as per norbitrial's answer:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
const result = arrOne.map(({id}) => arrTwo.find(e => {
const someCond = someConditionaEval();
if(someCond && e.id === id) {
return e;
} else {
return false;
}
}).val); //this breaks
Using .map() and .find() combination:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const result = arrOne.map(({id}) => arrTwo.find(e => e.id === id).val);
console.log(result);
I hope this helps!
You can use .filter() method on arrTwo and then using .includes() method get the filtered objects from arrTwo and then finally using .map() get only the val property values from each filtered object like:
var arrOne = [{id: 3},{id: 8},{id: 12}];
var arrTwo = [{id:1,val:"Adam"},{id:3,val:"Bailey"},{id:8,val:"Cathy"},{id:12,val:"David"},{id:15,val:"Earl"}];
var result = arrTwo.filter(a => arrOne.map(o=>o.id).includes(a.id)).map(o=>o.val)
console.log( result )
You could take an object with the values and then map the wanted values.
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }],
arrTwo = [{ id: 1, val: 'Adam' }, { id: 3, val: 'Bailey' }, { id: 8, val: 'Cathy' }, { id: 12, val: 'David' }, { id: 15, val: 'Earl' }],
values = arrTwo.reduce((r, { id, val }) => (r[id] = val, r), {}),
result = arrOne.map(({ id }) => values[id]);
console.log(result);
Create a Map of val by id from arrTwo, and then map arrOne, and extract the val from the Map using the id.
Why I prefer creating a Map/dictionary (object) instead of using Array.map() with Array.find()?
Because of the complexity - Array.map() with Array.find(), for example, is O(n * m), while creating a Map and then using Array.map() to get the values is O(n + m). However, if you've got two small arrays, this shouldn't actually hurt actual performance.
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const valById = new Map(arrTwo.map(({ id, val }) => [id, val]));
const result = arrOne.map(o => valById.get(o.id));
console.log(result);
Build an object from arrTwo to gather val's in one iteration.
use map on arrOne and get val from above object.
const update = (arr1, arr2) => {
const all = Object.fromEntries(arr2.map(({ id, val }) => [id, val]));
return arr1.map(({ id }) => all[id]);
};
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }];
var arrTwo = [
{ id: 1, val: "Adam" },
{ id: 3, val: "Bailey" },
{ id: 8, val: "Cathy" },
{ id: 12, val: "David" },
{ id: 15, val: "Earl" }
];
console.log(update(arrOne, arrTwo));

Lodash. How to get aggregate array from array objects

For example, I have an array:
const reference = [{id: 1, value: 10}, {id: 2, value: 10}, {id: 3, value: 10}, {id: 4, value: 5}];
How to get an array values from reference like
const result = [0, 10, 20, 25];
First step always = 0
Second step 0 + 10 = 10
Third step 0 + 10 + 10 = 20
Forth step 0 + 10 + 10 + 5 = 25
You can reduce the array, and add the current value to the last sum:
const reference = [{id: 1, value: 10}, {id: 2, value: 10}, {id: 3, value: 10}, {id: 4, value: 5}];
const result = reference
.reduce((r, o, i) => {
r.push(i === 0 ? 0 : r[r.length - 1] + o.value);
return r;
}, [])
console.log(result);
You could map the values by taking a closure over the sum and take zero for the first element.
const
reference = [{ id: 1, value: 10 }, { id: 2, value: 10 }, { id: 3, value: 10 }, { id: 4, value: 5 }],
result = reference.map((sum => ({ value }, i) => sum += i && value)(0));
console.log(result);
The way I would do this would be by using the Array.reduce method as follows :
let result = [0]
reference.reduce((acc, cur) => {
result.push(Object.values(cur)[1]+result[result.length-1])
})
Hope it helps

Filter and create a new array [duplicate]

This question already has answers here:
Using array map to filter results with if conditional
(5 answers)
Map and filter an array at the same time
(16 answers)
Closed 3 years ago.
I have an object as shown below:
var obj = [
{id: 1, name: 'AD', key: 10},
{id: 2, name: 'AD', key: 20},
{id: 3, name: 'BD', key: 30},
{id: 4, name: 'CD', key: 40}
];
I want to filter and create a new array which satisfies any condition. For eg: filter which have name as 'AD' and create a new array of key as:
[10, 20]
Tried .map
obj.map(ele => {
return ele.name === 'AD' ? ele.key : null;
}); //it adds even nulls in the result array as [10, 20, null, null]
Tried .filter:
obj.filter(ele => {
return ele.name === 'AD' ? ele.key : null;
});
Result: [{id: 1, name: "AD", key: 10}, {id: 2, name: "AD", key: 20}] //gives array of objects, not what I'm expecting.
Thanks in advance
First filter the array and then map to get the value:
obj.filter(e => e.name === 'AD').map(e => e.key)
Another option is flatmap (check browser compatibility here)
obj.flatMap(e => e.name === 'AD' ? [e.key] : [])
First of all obj is an array in your code.
Now the solution is simple you filter first and then map like this:
obj.filter(item => item.name === 'AD').map(item => item.key);
You can use reduce():
var obj = [{id: 1, name: 'AD', key: 10},{id: 2, name: 'AD', key: 20},{id: 3, name: 'BD', key: 30},{id: 4, name: 'CD', key: 40}];
var result = obj.reduce((acc, cur) => {
if (cur.name == "AD") acc.push(cur.key)
return acc;
}, []);
console.log(result);
var obj = [
{id: 1, name: 'AD', key: 10},
{id: 2, name: 'AD', key: 20},
{id: 3, name: 'BD', key: 30},
{id: 4, name: 'CD', key: 40}
];
function specialFilter(filt) {
return obj.filter(el => el.name === filt).map(el => el.key)
}
console.log(specialFilter("AD"))

Change the order of the objects in the array using a parameter

I have an array of object like that:
const arr = [
{number: 1, value: 50},
{number: 2, value: 4},
{number: 3, value: 33},
{number: 4, value: 49},
{number: 5, value: 92},
{number: 6, value: 1},
{number: 7, value: 49},
]
and:
const startBy = 3
then I want an other array like that:
const newArr = [
{number: 3, value: 33},
{number: 4, value: 49},
{number: 5, value: 92},
{number: 6, value: 1},
{number: 7, value: 49},
{number: 1, value: 50},
{number: 2, value: 4},
]
that is, I want to “rearrange” the elements of the array such that the first element is the one with the field number equal to startBy and then the next in order.
The field number must always be in order, only that can vary from which item to start.
I don't know how I can do this thing..
You could take the value as check for a delta if the value is smaller than the given value. Otherwise sort by delta.
function sortBy(array, first) {
array.sort(function (a, b) {
return (a.number < first) - (b.number < first) || a.number - b.number;
});
return array;
}
const array = [{ number: 1, value: 50 }, { number: 2, value: 4 }, { number: 3, value: 33 }, { number: 4, value: 49 }, { number: 5, value: 92 }, { number: 6, value: 1 }, { number: 7, value: 49 }];
console.log(sortBy(array, 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Pretty easy:
Just sort the array,
then search where to split your array with a
findIndex,
and finally slice the array in two part to create a
new one ;)
const arr = [
{number: 1, value: 50},
{number: 2, value: 4},
{number: 3, value: 33},
{number: 4, value: 49},
{number: 5, value: 92},
{number: 6, value: 1},
{number: 7, value: 49},
];
const startBy = 3;
//Presorting the input array.
const sortedArray = arr.sort((a, b) => a.number-b.number);
//Finding the split index.
const splitIndex = sortedArray.findIndex(elt => elt.number === startBy);
//Slicing the array and creating a new one.
const result = [...sortedArray.slice(splitIndex), ...sortedArray.slice(0, splitIndex)];
console.log(result);
Well, you could just take out the first element of the sequence, and add it to the end until you've found the correct element.
example:
const arr = [
{number: 1, value: 50},
{number: 2, value: 4},
{number: 3, value: 33},
{number: 4, value: 49},
{number: 5, value: 92},
{number: 6, value: 1},
{number: 7, value: 49},
]
undefined
function ReturnNewArray(array, value){
while(array[0].number!=value){
array.push(array.shift())
}
return array;
}
undefined
ReturnNewArray(arr,3);
0: {number: 3, value: 33}
1: {number: 4, value: 49}
2: {number: 5, value: 92}
3: {number: 6, value: 1}
4: {number: 7, value: 49}
5: {number: 1, value: 50}
6: {number: 2, value: 4}
Assuming your initial array is sorted by the number property you could check if that property is less than your startBy and if the condition is true remove that element and push it
arr.map(o => {
if (o.number < startBy) {
arr.push(arr.shift());
}
});
console.log(arr);
Codepen demo
You have to simply sort your array based on your number key . More About sort
const arr = [
{number: 1, value: 50},
{number: 2, value: 4},
{number: 3, value: 33},
{number: 4, value: 49},
{number: 5, value: 92},
{number: 6, value: 1},
{number: 7, value: 49},
]
arr.sort((a,b) => {
return (a.number < 3) - (b.number < 3) || (a.number-b.number)
})
console.log(arr);
You can also find where the element is in the array, and shift the original array that many positions:
const arr = [{
number: 1,
value: 50
},
{
number: 2,
value: 4
},
{
number: 3,
value: 33
},
{
number: 4,
value: 49
},
{
number: 5,
value: 92
},
{
number: 6,
value: 1
},
{
number: 7,
value: 49
},
]
const findPosition = (array, startBy) => {
for (let i = 0; i < array.length; i++) {
if (array[i].number == startBy) {
return i;
}
}
}
const shiftArray = (array, positions) => {
for (let i = 0; i < positions; i++) {
const first = array.shift();
array.push(first);
}
return array;
}
console.log(shiftArray(arr, findPosition(arr, 3)));
Comments Inline with code
let arr = [{number: 1, value: 50},{number: 2, value: 4},{number: 3, value: 33},{number: 4, value: 49},{number: 5, value: 92},{number: 6, value: 1},{number: 7, value: 49}]
// sort the array by number value
arr.sort((a,b) => a.number - b. number)
// push into new array via reduce every element until you find your starting number and then from there insert everything thereafter into new array at starting position by Array.unshift()
function startFromNumber(number) {
return arr.slice(0).reduce((a, d, i, arrC) =>
(d.number == number
? (a.unshift(...arrC.slice(i)), arrC.length = 0)
: a.push(d)
, a), [])
}
console.log(startFromNumber(3))
console.log(startFromNumber(5))

Javascript compare multiple arrays of objects

How can I compare multiple arrays of objects and add new properties with the number of occurrences an object was found and the array indexes where the object was found?
The object comparison must be made by the name property.
Example:
var arrays = [
[
{
name: 'aa',
value: 1
},
{
name: 'ab',
value: 2
},
{
name: 'ac',
value: 3
},
{
name: 'aa',
value: 1
}
],
[
{
name: 'aa',
value: 1
},
{
name: 'ab',
value: 2
},
],
[
{
name: 'ac',
value: 3
},
{
name: 'aa',
value: 1
}
]
]
After execution the object from the above array should have these properties:
[
[
{
name: 'aa',
value: 1,
occurrences: 3,
where: [0, 1, 2]
},
{
name: 'ab',
value: 2,
occurrences: 2,
where: [0, 1]
},
{
name: 'ac',
value: 3,
occurrences: 2,
where: [0, 2]
},
{
name: 'aa',
value: 1,
occurrences: 3,
where: [0, 1, 2]
}
],
[
{
name: 'aa',
value: 1,
occurrences: 3,
where: [0, 1, 2]
},
{
name: 'ab',
value: 2,
occurrences: 2,
where: [0, 1]
}
],
[
{
name: 'ac',
value: 3,
occurrences: 2,
where: [0, 2]
},
{
name: 'aa',
value: 1,
occurrences: 3,
where: [0, 1, 2]
}
]
]
Basically I want to check if the object with a specific name property exists in the other arrays.
This is the solution that comes in my mind:
1. Loop through the array that has the most objects
2. Loop through each object
3. Loop through the other arrays and apply Array.prototype.find()
But this will take a lot of time since each of my array will have at least 500 objects...
You can use array#reduce to get the number of occurrences and the index of occurrences in an object.
Then, you can modify your object in the arrays by simply using Object.assign() and adding the where and occurrences property.
var arrays = [ [ { name: 'aa', value: 1 }, { name: 'ab', value: 2 }, { name: 'ac', value: 3 }, { name: 'aa', value: 1 } ], [ { name: 'aa', value: 1 }, { name: 'ab', value: 2 }, ], [ { name: 'ac', value: 3 }, { name: 'aa', value: 1 } ] ];
var result = arrays.reduce((res, arr, index) => {
arr.forEach(({name,value}) => {
res[name] = res[name] || {occurrences: 0};
res[name]['where'] = res[name]['where'] || [];
if(!res[name]['where'].includes(index)){
res[name]['where'].push(index);
res[name].occurrences += 1;
}
});
return res;
},{});
arrays.forEach(arr => arr.forEach(obj => Object.assign(obj, result[obj.name])));
console.log(arrays);
This looked like trivial reduce, until i noticed nested arrays ) so it's more like flatten + reduce, with memory.
Code below is doing what you need, just the prop names are short (as i type them on the phone):
let f = (ai, a,v,i,m) => {
if (!a[v.id]) {
a[v.id] = {id: v.id, v: v.name, count: 1, at: [ai]};
} else {
a[v.id].count += 1;
a[v.id].at.push(ai);
}
return a;
};
let r = [[{id: 'aa', value: 42}], [{id: 'ba', value: 11}, {id: 'aa', value: 42}]]
.reduce ((a, v, i) => v.reduce (f.bind (null, i),a), {});
console.log (r);
Code still visits each element in any array only once, so complexity is O(n), and there should not be a problem running it on arrays up to a million of total elements (e.g. 1000 arrays of 1000 elements, or 200 x 5000).

Categories