Related
i have the following arrays
array1 = [
{a:{key:1 , value: 10} , b:{key:1 , value:12} , c:{key:1 , value: 5} , d:{key:1 , value:2}},
{a:{key:2 , value: 10} , b:{key:2 , value:12} , c:{key:2 , value: 5} , d:{key:2 , value:2}},
{a:{key:3 , value: 10} , b:{key:3 , value:12} , c:{key:3 , value: 5} , d:{key:3 , value:2}},
]
array2 = [
{a:{key:1 , value: 10} , b:{key:1 , value:12} , c:{key:1 , value: 5} , d:{key:1 , value:2}},
{a:{key:2 , value: 10} , b:{key:2 , value:12} , c:{key:2 , value: 5} , d:{key:2 , value:2}},
{a:{key:4 , value: 10} , b:{key:4 , value:12} , c:{key:4 , value: 5} , d:{key:4 , value:2}},
]
reduced array based on key should look like this:
combinedArray= [
{a:{key:1 , value: 20} , b:{key:1 , value:24} , c:{key:1 , value: 10} , d:{key:1 , value:4}},
{a:{key:2 , value: 20} , b:{key:2 , value:24} , c:{key:2 , value: 10} , d:{key:2 , value:4}},
{a:{key:3 , value: 10} , b:{key:3 , value:12} , c:{key:3 , value: 5} , d:{key:3 , value:2}},
{a:{key:4 , value: 10} , b:{key:4 , value:12} , c:{key:4 , value: 5} , d:{key:4 , value:2}},
]
first i tried to merge the two arrays using const mergedArray = [...array1, ...array2]
now i want to check for key duplicates. for example, if there is key1 in both array1 and array2, remove the duplicates then combine the values of that key.
this is what i have tried but it is only iterating through a.key only:
function kdeAdder(param) {
const array = [param.a]
let tempHistory = [];
for(let x=0;x<array.length;x++){
array[x].forEach((item)=>{
let noMatch = true;
if(tempHistory.length > 0) {
tempHistory.forEach((tempItem, i)=>{
if(item.key === tempItem.key) {
tempHistory[i].value += item.value;
noMatch = !noMatch;
}
});
}
return (noMatch) ? tempHistory.push(item) : null;
});
}
return tempHistory;
}
kdeAdder(mergedArray);
As you confirmed the key inner property is commonly shared by the four "a", "b", "c", "d" objects in an outer object, the a.key value can be used to identify which outer objects should merge.
You could group all objects (irrespective of whether they occur in array1 or array2) by that a.key, and then aggregate objects that occur in the same group. Both of these actions can be accomplished with a reduce call:
const aggregate = (objects) =>
objects.reduce((x, y) => ({
a: { key: x.a.key, value: x.a.value + y.a.value },
b: { key: x.b.key, value: x.b.value + y.b.value },
c: { key: x.c.key, value: x.c.value + y.c.value },
d: { key: x.d.key, value: x.d.value + y.d.value },
}));
const merge = (array1, array2) =>
Object.values(array1.concat(array2).reduce((acc, obj) => {
(acc[obj.a.key] ??= []).push(obj);
return acc;
}, {})).map(aggregate);
const array1 = [
{a:{key:1 , value: 10} , b:{key:1 , value:12} , c:{key:1 , value: 5} , d:{key:1 , value:2}},
{a:{key:2 , value: 10} , b:{key:2 , value:12} , c:{key:2 , value: 5} , d:{key:2 , value:2}},
{a:{key:3 , value: 10} , b:{key:3 , value:12} , c:{key:3 , value: 5} , d:{key:3 , value:2}},
];
const array2 = [
{a:{key:1 , value: 10} , b:{key:1 , value:12} , c:{key:1 , value: 5} , d:{key:1 , value:2}},
{a:{key:2 , value: 10} , b:{key:2 , value:12} , c:{key:2 , value: 5} , d:{key:2 , value:2}},
{a:{key:4 , value: 10} , b:{key:4 , value:12} , c:{key:4 , value: 5} , d:{key:4 , value:2}},
]
console.log(merge(array1, array2));
You can first reduce the output to a single object since its a sort of accumulation of numbers, and then get the format you want as the second step.
const array1 = [ { a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 }, }, { a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 }, }, { a: { key: 3, value: 10 }, b: { key: 3, value: 12 }, c: { key: 3, value: 5 }, d: { key: 3, value: 2 }, }, ]; const array2 = [ { a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 }, }, { a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 }, }, { a: { key: 4, value: 10 }, b: { key: 4, value: 12 }, c: { key: 4, value: 5 }, d: { key: 4, value: 2 }, }, ];
const mergedArray = [...array1, ...array2];
const keys = []
const reducedOutput = mergedArray.reduce((prev, curr) => {
Object.entries(curr).forEach(([mainKey, { key, value }]) => {
// mainKey is a, b, c, d in your case
if (!prev[mainKey]) {
prev[mainKey] = {};
}
// key is 1, 2, 3, 4 in your case
if (!keys.includes(key)) {
keys.push(key)
}
prev[mainKey][key] = prev[mainKey][key]
? prev[mainKey][key] + value
: value;
});
return prev;
}, {});
const output = keys.map(key => {
const obj = {}
Object.entries(reducedOutput).forEach(([k, v]) => {
obj[k] = {key, value: v[key]}
})
return obj
})
console.log(output)
This will work with any other keys for a, b, c, d keys and 1, 2, 3, 4 keys you have used in two levels.
Using Object.entries(), Array.prototype.reduce(), Array.prototype.forEach(), and Array.prototype.map()
The following provided code implements an approach which tries to find a balance in between 1st being agnostic to any array item's current and future structure except for both property names, key and value, of any array item's second level structure and 2nd how to handle the merger of other unknown second level data.
Therefore the general approach creates an object based lookup from the shorter sourceList where each item gets referred to via the value of its second level key property, whereas the longer targetList will be reduced in order to create the final result of merged items from both arrays.
Since approach and implementation are unaware of an items first level structure, one has to reduce again all of a currently processed item's entries. For each of a target item's unknown entry one can rely on such an entry's 2nd level properties, key and value. From all the available data, either known or unknown, one can aggregate the common merger of both the source- and the target-item; their values will be totaled and both of their unknown rest data will be merged by spread syntax, where the latter is the approach's trade off/compromise.
function aggregateFirstValueKeyBasedLookup(lookup, item) {
lookup[Object.values(item)[0]?.key ?? ''] = item;
return lookup;
}
function createKeyBasedValueMergerFromSourceLookup(
{ lookup = {}, result = [] }, targetItem, idx, arr,
) {
let currentLookupKey;
result.push(Object
.entries(targetItem)
.reduce((merger, [
targetEntryKey, {
key, value: targetEntryValue = 0, ...targetEntryRest
}
]) => {
currentLookupKey = key;
const sourceItem = lookup[key] ?? {};
const {
value: sourceEntryValue = 0, ...sourceEntryRest
} = sourceItem[targetEntryKey] ?? {};
return Object.assign(merger, {
[ targetEntryKey ]: {
key,
value: (targetEntryValue + sourceEntryValue),
...targetEntryRest,
...sourceEntryRest,
},
});
}, {})
);
// delete already processed source-items from lookup.
Reflect.deleteProperty(lookup, currentLookupKey);
if (idx >= arr.length - 1) {
// finalize the result by ...
result.push(
// ...pushing all of the lookup's
// unprocessed source-items.
...[...Object.values(lookup)]
);
}
return { lookup, result };
}
const array1 = [{
a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 }
}, {
a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 }
}, {
a: { key: 3, value: 10 }, b: { key: 3, value: 12 }, c: { key: 3, value: 5 }, d: { key: 3, value: 2 }
}];
const array2 = [{
a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 }
}, {
a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 }
}, {
a: { key: 4, value: 10 }, b: { key: 4, value: 12 }, c: { key: 4, value: 5 }, d: { key: 4, value: 2 }
}];
const [ targetList, sourceList ]
= [array1, array2].sort((a, b) => b.length - a.length);
const sourceLookup = sourceList
.reduce(aggregateFirstValueKeyBasedLookup, Object.create(null));
console.log({ sourceLookup });
const { result: mergedItemList } = targetList
.reduce(createKeyBasedValueMergerFromSourceLookup, {
lookup: sourceLookup, result: [],
});
console.log({ mergedItemList });
// - changed item structure which keeps
// just the most necessary pattern.
const newItemStructureList1 = [{
quick: { key: 'foo', value: 33, biz: 'biz' },
brown: { key: 'foo', value: 22, baz: 'baz' },
fox: { key: 'foo', value: 11, buzz: 'buzz' },
}, {
quick: { key: 'bar', value: 11, baz: 'baz' },
brown: { key: 'bar', value: 33, biz: 'biz' },
fox: { key: 'bar', value: 22, booz: 'booz' },
}, {
quick: { key: 'baz', value: 22, baz: 'baz' },
brown: { key: 'baz', value: 11, biz: 'biz' },
fox: { key: 'baz', value: 33, booz: 'booz' },
}];
const newItemStructureList2 = [{
brown: { key: 'foo', value: 11, baz: 'baz' },
fox: { key: 'foo', value: 33, booz: 'booz' },
quick: { key: 'foo', value: 22, baz: 'baz' },
}, {
fox: { key: 'baz', value: 33, buzz: 'buzz' },
quick: { key: 'baz', value: 11, biz: 'biz' },
brown: { key: 'baz', value: 33, baz: 'baz' },
}];
const [ target, source ]
= [newItemStructureList1, newItemStructureList2].sort((a, b) => b.length - a.length);
const lookup = source
.reduce(aggregateFirstValueKeyBasedLookup, Object.create(null));
console.log({ lookup });
const { result: mergedItems } = target
.reduce(createKeyBasedValueMergerFromSourceLookup, { lookup, result: [] });
console.log({ mergedItems });
.as-console-wrapper { min-height: 100%!important; top: 0; }
You could group with the key property and the outer property.
const
array1 = [{ a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 } }, { a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 } }, { a: { key: 3, value: 10 }, b: { key: 3, value: 12 }, c: { key: 3, value: 5 }, d: { key: 3, value: 2 } }],
array2 = [{ a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 } }, { a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 } }, { a: { key: 4, value: 10 }, b: { key: 4, value: 12 }, c: { key: 4, value: 5 }, d: { key: 4, value: 2 } }],
result = Object.values([...array1, ...array2].reduce((r, o) => {
Object.entries(o).forEach(([k, { key, value }]) => {
r[key] ??= {};
r[key][k] ??= { key, value: 0 };
r[key][k].value += value;
});
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I need to multiply all the "values" inside "obj1" with the "percent' inside obj2 based on the id of each object. What would be the best way to do that? I've tried with for loop and reduce but I wasn't successful. Any help will be appreciated.
const obj1 = [ { id: 1, value: 10 }, { id: 2, value: 10 } ]
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
}
}
outputExpected: [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]
You can do that by going through and matching the ids. there are some optimizations that can be made if they are sorted however.
const obj1 = [ { id: 1, value: 10 }, { id: 2, value: 10 } ]
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
}
}
const x = Object.keys(obj2).map((key,index)=>{
const { id, value } = obj1.find(({id})=>id===obj2[key].id)
return ({id,value:value*obj2[key].percent})
})
console.log(x)
//outputExpected: [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]
You can first create a lookup map using Map, then loop over the obj1 using map to get the desired result
const obj1 = [
{ id: 1, value: 10 },
{ id: 2, value: 10 },
];
const obj2 = {
len: {
id: 1,
nj: "321345",
percent: 0.05,
},
wor: {
id: 2,
nj: "321345",
percent: 0.1,
},
};
const map = new Map();
Object.values(obj2).forEach((v) => map.set(v.id, v));
const result = obj1.map((o) => ({ ...o, value: o.value * map.get(o.id).percent }));
console.log(result);
This should work for you but doesnt handle exeptions if the id doesnt exist in both objects.
// First get the values in an array for easier manipulation
const aux = Object.values(obj2)
const output = obj1.map(ob => {
// Find the id in the other array.
const obj2Ob = aux.find(o => o.id === ob.id) // The id must exist in this aproach
return {
id: ob.id,
value: ob.value * obj2Ob.percent
}
})
console.log(output) // [ { id: 1, value: 0.5 }, { id: 2, value: 1 } ]
I have javascript array object as below. My need is to sum value base on seach id in the array object.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
],
For example sum of value for id 1 is 10 + 30 + 25 + 20 = 85 , It may be something link linq but I'm not sure in javascript. Thanks for all answers.
You can use a combination of filter and reduce to get the result you want:
sumOfId = (id) => array.filter(i => i.id === id).reduce((a, b) => a + b.val, 0);
Usage:
const sumOf1 = sumOfId(1); //85
Reading material:
Array.prototype.filter
Array.prototype.reduce
A way to do it with a traditional for loop
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sums = {};
for (var i = 0; i < array.length; i++) {
var obj = array[i];
sums[obj.id] = sums[obj.id] === undefined ? 0 : sums[obj.id];
sums[obj.id] += parseInt(obj.val);
}
console.log(sums);
running example
You can use reduce() and findIndex()
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
let res = array.reduce((ac,a) => {
let ind = ac.findIndex(x => x.id === a.id);
ind === -1 ? ac.push(a) : ac[ind].val += a.val;
return ac;
},[])
console.log(res);
JS noob here ... I guess something like this should be here too :-)
let newArray = {}
array.forEach((e) => {
!newArray[e.id] && (newArray[e.id] = 0);
newArray[e.id] += e.val;
});
You can loop on the array and check the ids.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sum = 0;
var id = 1;
$.each(array, function(index, object){
if (object.id == id) {
sum += object.val;
}
});
console.log(sum);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Using Array#reduce and Map you can get the sum for each id like so. This also uses destructuring to have quicker access to properties.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
console.log(res.get(1));
console.log(res.get(2));
If you wanted to output all the sums, then you need to use Array#from
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
);
console.log(res);
If the format should be similar as to your original structure, you need to add a Array#map afterwards to transform it.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
).map(([id,sum])=>({id,sum}));
console.log(res);
You could take GroupBy from linq.js with a summing function.
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }],
result = Enumerable
.From(array)
.GroupBy(null, null, "{ id: $.id, sum: $$.Sum('$.val') }", "$.id")
.ToArray();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>
Here is another option, introducing an Array.prototype.sum helper:
Array.prototype.sum = function (init = 0, fn = obj => obj) {
if (typeof init === 'function') {
fn = init;
init = 0;
}
return this.reduce(
(acc, ...fnArgs) => acc + fn(...fnArgs),
init
);
};
// .sum usage examples
console.log(
// sum simple values
[1, 2, 3].sum(),
// sum simple values with initial value
[1, 2, 3].sum(10),
// sum objects
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(obj => obj.a),
// sum objects with initial value
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(10, obj => obj.a),
// sum custom combinations
[{ amount: 1, price: 2 }, { amount: 3, price: 4 }]
.sum(product => product.amount * product.price)
);
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }];
// solutions
console.log(
array.filter(obj => obj.id === 1).sum(obj => obj.val),
array.filter(({id}) => id === 1).sum(({val}) => val),
array.sum(({id, val}) => id === 1 ? val : 0)
);
references:
Array.prototype.reduce
Array.prototype.filter
Arrow functions used in sum(obj => obj.val)
Object destructing assignment used in ({id}) => id === 1
Rest parameters used in (acc, ...fnArgs) => acc + fn(...fnArgs)
Conditional (ternary) operator used in id === 1 ? val : 0
I would like to filter an array of objects according to the highest value of "value" key and distinct each object by their "id" key.
Example :
var array = [
{
id: 1,
value: 10
},
{
id: 1,
value: 2
},
{
id: 2,
value: 6
},
{
id: 2,
value: 5
},
{
id: 2,
value: 1
}
]
And the expected output:
array = [
{
id: 1,
value: 10
},
{
id: 2,
value: 6
}
]
Thanks
You could reduce the array and check if an object exist with the same id and update if necessary. This approach takes the objects with the largest value.
const
array = [{ id: 1, value: 10 }, { id: 1, value: 2 }, { id: 2, value: 6 }, { id: 2, value: 5 }, { id: 2, value: 1 }],
result = array.reduce((r, o) => {
const index = r.findIndex(({ id }) => o.id === id);
if (index === -1) r.push(o);
else if (r[index].value < o.value) r[index] = o;
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Another approach with a Map.
const
array = [{ id: 1, value: 10 }, { id: 1, value: 2 }, { id: 2, value: 6 }, { id: 2, value: 5 }, { id: 2, value: 1 }],
result = Array.from(
array.reduce((m, { id, value }) => m.set(id, m.has(id)
? Math.max(m.get(id), value)
: value
), new Map),
([id, value]) => ({ id, value })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is a possible way to doing this
var array = [
{
id: 1,
value: 10
},
{
id: 1,
value: 2
},
{
id: 2,
value: 6
},
{
id: 2,
value: 5
},
{
id: 2,
value: 1
}
]
let objdis = {};
array.forEach(val => {
if(objdis[val.id] !== undefined){
objdis[val.id].push(Number(val.value));
}else{
objdis[val.id] = [];
objdis[val.id].push(val.value);
}
})
let returningObj = [];
for (prop in objdis){
let obj = {};
obj.id = Number(prop);
obj.Value = Math.max(...objdis[prop])
returningObj.push(obj);
}
console.log(returningObj);
i hope this helps
var arr = [
{
id: 1,
value: 10
},
{
id: 1,
value: 2
},
{
id: 2,
value: 6
},
{
id: 2,
value: 5
},
{
id: 2,
value: 1
}
];
const output = Object.values(arr.reduce((x, y) => {
x[y.id] = x[y.id] && x[y.id].value > y.value ? x[y.id] : y
return x
}, {}));
console.log(output);
There's my answer, but it needs to have sorted indexes and isn't as cool as reduce examples You got here.
var array = [
{
id: 1,
value: 10
},
{
id: 1,
value: 1
},
{
id: 2,
value: 1
},
{
id: 2,
value: 5
},
{
id: 2,
value: 35
},
{
id: 3,
value: 4
},
{
id: 3,
value: 14
},
{
id: 2,
value: 123
}
];
function getHighestValues(Arr) {
var newArr = [];
var IdArr = Arr.map(elem => elem.id);
IdArr.sort((a,b)=>a-b);
for(var i = 0; i < Arr.length; i++) {
let currIdIndex = IdArr.indexOf(Arr[i].id);
if(newArr[currIdIndex] == undefined) {
newArr.push(
{
id: Arr[i].id,
value: Arr[i].value
})
} else if(newArr[currIdIndex].value < Arr[i].value) {
newArr[currIdIndex].value = Arr[i].value;
}
IdArr.splice(currIdIndex, 1);
if(IdArr.indexOf(Arr[i].id) == -1) {
IdArr.splice(currIdIndex, 0, Arr[i].id);
}
}
return newArr;
}
array = getHighestValues(array);
console.log(array);
I have an array of objects with the following structure:
var varientSections = [
{
type: "frame",
values: ["black", "white", "wood"]
},
{
type: "finish",
values: ["matte", "glossy"]
}
];
I want to get the combination of the array values and create a new list with it. Right now, I am able to retrieve the combination from the nested array values using the method called getCombination(varientSections). However, I do not know how to create a new list with the following structure:
var results = [
{
attributes: [
{
type: "frame",
value: "black"
},
{
type: "finish",
value: "matte"
}
]
},
{
attributes: [
{
type: "frame",
value: "black"
},
{
type: "finish",
value: "glossy"
}
]
},
{
attributes: [
{
type: "frame",
value: "white"
},
{
type: "finish",
value: "matte"
}
]
},
{
attributes: [
{
type: "frame",
value: "white"
},
{
type: "finish",
value: "glossy"
}
]
},
{
attributes: [
{
type: "frame",
value: "wood"
},
{
type: "finish",
value: "matte"
}
]
},
{
attributes: [
{
type: "frame",
value: "wood"
},
{
type: "finish",
value: "glossy"
}
]
}
];
Below is my code:
function getCombinations(arr) {
if (arr.length === 0) {
return [[]];
}
let [current, ...rest] = arr;
let combinations = getCombinations(rest);
var result = current.values.reduce(
(accumulator, currentValue) => [
...accumulator,
...combinations.map(c => [currentValue, ...c])
],
[]
);
console.log("result is ");
console.log(result);
return result;
}
let varientCombinations = getCombinations(varientSections);
console.log(varientCombinations);
let updatedVarientDetails = [];
varientSections.forEach((varientSection, index) => {
let type = varientSection.type;
varientCombinations.forEach(combination => {
let obj = [
{
type: type,
value: combination[index]
},
];
updatedVarientDetails.push(obj);
});
});
console.log(updatedVarientDetails);
You could get the cartesian product and give it later the wanted style. The names and values are taken form the handed over object.
The algorithm takes all key/value pairs and has a stric view to the values, that means if an array is found or an object, hence w && typeof w === "object", the actual part is taken an used for adding additional key/value pairs.
For example a small object with two properties
{ a: 1, b: [2, 3] }
yields
[
{ a: 1, b: 2 },
{ a: 1, b: 3 }
]
A bit more advanced object, like
{ a: 1, b: { c: { d: [2, 3], e: [4, 5] } } }
yields the same structure as given
[
{
a: 1,
b: {
c: { d: 2, e: 4 }
}
},
{
a: 1,
b: {
c: { d: 2, e: 5 }
}
},
{
a: 1,
b: {
c: { d: 3, e: 4 }
}
},
{
a: 1,
b: {
c: { d: 3, e: 5 }
}
}
]
Thant means, from any found sub object the cartesian product is taken and combined with the actual values.
const
getCartesian = object => Object.entries(object).reduce(
(r, [key, value]) => {
let temp = [];
r.forEach(s =>
(Array.isArray(value) ? value : [value]).forEach(w =>
(w && typeof w === "object" ? getCartesian(w) : [w]).forEach(x =>
temp.push({ ...s, [key]: x })
)
)
);
return temp;
},
[{}]
),
data = [{ type: "frame", value: ["black", "white", "wood"] }, { type: "finish", value: ["matte", "glossy"] }],
result = getCartesian(data)
.map(o => ({ attributes: Object.assign([], o).map(({ ...o }) => o) }));
console.log(result);
console.log(getCartesian({ a: 1, b: { c: { d: [2, 3], e: [4, 5] } } }));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could simplify it to this:
var variantSections = [
{
type: "frame",
values: ["black", "white", "wood"]
},
{
type: "finish",
values: ["matte", "glossy"]
}
];
// iterate through each variantSection and create objects like {"type": "frame", "value": "black"}
var sections = variantSections.map(variant => {
return variant.values.map(val => ({type: variant.type, value: val}))
});
// then iterate through the two resulting arrays of objects, combining each into the attributes object you want
var results = [];
for (var i = 0; i < sections[0].length; i++) {
for (var j = 0; j < sections[1].length; j++) {
results.push({attributes: [sections[0][i], sections[1][j]]});
}
}
console.log(JSON.parse(JSON.stringify(results)));