Divide object array elements into groups of n each javascript - javascript

I have an Object as below:
const boxOfFruits = {
apples: [
{
name: "Kashmiri",
},
{
name: "Washington",
},
{
name: "Himalayan",
},
{
name: "Fuji",
}
],
oranges: [
{
name: "Nagpur",
},
{
name: "Clementine",
},
],
mangoes: [
{
name: "Totapuri",
},
{
name: "Alphonso",
},
{
name: "Langda",
},
],
}
I want to divide these fruits into boxes; maximum of n each, let's say where n is 3 and apples, oranges and mangoes are equally distributed.
So the output in this case would be:
box_1 = [{name: "Kashmiri"}, {name: "Nagpur"},{name: "Totapuri"}];
box_2 = [{name: "Washington"}, {name: "Clementine"},{name: "Alphonso"}];
box_3 = [{name: "Himalayan"},{name: "Langda"}, {name: "Fuji"}];
The type of fruits(apple,oranges,etc)/keys in object can increase/decrease and n is also variable. In case total fruits are less than n, then it would be just 1 box of fruits.
What I have tried so far:
Using Lodash, I am calculating the minimum and the maximum fruits in a single type:
const minFruitType = _.min(Object.values(basket).map((eachBasket: any) => eachBasket.length));
Total teams will the sum of the fruits / n
Will distribute the minimum fruits (l) in the first l boxes and fill the rest with the remaining fruits at every iteration while at the start of every iteration will calculate the minimum type of fruits again.

You can use Object.values(), array#reduce and array#forEach to transform your object.
const boxOfFruits = { apples: [ { name: "Kashmiri", }, { name: "Washington", }, { name: "Himalayan", }, ], oranges: [ { name: "Nagpur", }, { name: "Clementine", }, ], mangoes: [ { name: "Totapuri", }, { name: "Alphonso", }, { name: "Langda", }, ], },
result = Object.values(boxOfFruits).reduce((r, arr) => {
arr.forEach((o,i) => {
const key = `box_${i+1}`;
r[key] ??= r[key] || [];
r[key].push(o)
});
return r;
},{});
console.log(result);

The easiest way would be to use lodash.js's zip() function:
const boxes = _.zip( Object.values(boxOfFruits) );
Note that _.zip() will give you undefined values when the source arrays are different lengths, so you'll need/want to filter those out:
const boxes == _.zip( Object.values(boxOfFruits) )
.map(
box => box.filter(
x => x !== undefined
)
);
But that will not distribute the fruits evenly. For that, it shouldn't get much for difficult than this:
function distribute(boxOfFruits, n) {
const boxes = [];
const fruits = Object.keys(boxOfFruits);
for ( const fruit of fruits ) {
let i = 0;
const items = boxOfFruits[fruit];
for (const item of items) {
boxes[i] = !boxes[i] ?? [];
boxes[i] = boxes[i].push(item);
++i;
i = i < n ? i : 0 ;
}
}
return boxes;
}

A modified version of #Nicholas Carey's answer worked for me:
function distribute(boxOfFruits, n) {
let boxes = [];
let totalFruits = Object.values(boxOfFruits)
.reduce((content, current) => content + current.length, 0);
let maxBoxes = Math.ceil(totalFruits / 4);
Object.values(boxOfFruits).forEach((fruits) => {
let i = 0;
fruits.forEach((fruit) => {
boxes[i] ??= boxes[i] || [];
boxes[i].push(fruit);
++i;
i = i < (n+1) ? i : 0;
});
});
// Extra boxes created, redistribute them to
// starting boxes
let newBoxes = teams.slice(0, maxBoxes);
let pendingBoxes = teams.slice(maxBoxes);
let pendingFruits = pendingBoxes.flat();
let distributedBoxes = newBoxes.map((eachBox) => {
let required = n - eachBox.length;
if (required > 0) {
eachBox.push(...pendingFruits.splice(0, required));
}
return eachBox;
});
return distributedBoxes;
}
Code is pretty much the same as Nicholas's accept the below changes:
Directly fetched the values and iterated over those
empty array creation was failing, this way works
and checking on the max box size with n+1 instead of n

Related

Combining objects if condition matches

I have an array boxSize that has a total count of boxes for small and large boxes. I would like to combine these 2 separate objects into one if they have the same date and warehouse name
For example here is the boxSize array:
const boxSize = [
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'small',
total: 5
},
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'large',
total: 9
}
]
I attempted to loop through the array like this in the code snippet below:
const boxSize = [
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'small',
total: 5
},
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'large',
total: 9
}
]
var obj = {};
for(let i = 0; i < boxSize.length; i++){
var date = boxSize[i].deliveryWeek;
// Get previous date saved inside the result
var p_date = obj[date] || {};
// Merge the previous date with the next date
obj[date] = Object.assign(p_date, boxSize[i]);
}
// Convert to an array
var result = Object.values(obj);
console.log(result);
I am having trouble coming up with the logic and finding up examples to meet these requirements condition.
How can I end up with an array that looks something similar to this when the objects have the same warehouse and deliveryWeek:
const boxSize = [
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSizeSmall: 'small',
smallTotal: 5,
boxSizeLarge: 'large',
largeTotal: 9
}
]
console.log(boxSize)
You could get a result like that by aggregating them with a map and then doing some transformations on the values like this:
const boxSize = [{warehouse:'NYC',deliveryWeek:'2022-11-07',boxSize:'small',total:5},{warehouse:'NYC',deliveryWeek:'2022-11-07',boxSize:'large',total:9}];
const keyFor = (item) => `${item.warehouse}:${item.deliveryWeek}`;
const map = new Map();
boxSize.forEach((box) => {
const key = keyFor(box);
if (!map.has(key)) map.set(key, []);
map.get(key).push(box);
});
const result = Array.from(map).map(([, values]) => ({
warehouse: values[0].warehouse,
deliveryWeek: values[0].deliveryWeek,
...values.reduce((obj, item) => ({
...obj,
["boxSize" + item.boxSize[0].toUpperCase() + item.boxSize.slice(1)]: item.boxSize,
[item.boxSize + "Total"]: item.total,
}), {}),
}));
console.log(result);
but this is really complicated and it's hard to use the resulting object in practice. Also, duplicate entries will be overwritten by the latest entry... I recommend using an array in the resulting object to avoid all of these:
const boxSize = [{warehouse:'NYC',deliveryWeek:'2022-11-07',boxSize:'small',total:5},{warehouse:'NYC',deliveryWeek:'2022-11-07',boxSize:'large',total:9}];
const keyFor = (item) => `${item.warehouse}:${item.deliveryWeek}`;
const map = new Map();
boxSize.forEach((box) => {
const key = keyFor(box);
if (!map.has(key)) map.set(key, []);
map.get(key).push(box);
});
const result = Array.from(map).map(([, values]) => ({
warehouse: values[0].warehouse,
deliveryWeek: values[0].deliveryWeek,
details: values.map(({ boxSize, total }) => ({ boxSize, total })),
}));
console.log(result);
which is arguably easier to implement and use compared to the former.
let boxSize = [
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'small',
total: 5
},
{
warehouse: 'NYC',
deliveryWeek: '2022-11-07',
boxSize: 'large',
total: 9
}
]
let combinedBoxes = [];
for(let i = 0; i < boxSize.length; i++){
let currentBox = boxSize[i];
let boxes = boxSize.filter(box => box.warehouse == currentBox.warehouse && box.deliveryWeek == currentBox.deliveryWeek)
let small = boxes.filter(box => box.boxSize == "small")[0]
let large = boxes.filter(box => box.boxSize == "large")[0]
combinedBoxes.push({
warehouse: currentBox.warehouse,
deliveryWeek: currentBox.deliveryWeek,
smallTotal: small.total,
largeTotal: large.total
})
boxSize = boxSize.filter(box => box.warehouse != currentBox.warehouse && box.deliveryWeek != currentBox.deliveryWeek)
}
console.log(combinedBoxes)

React.js: How to find duplicates for properties in an array of object and put a progressive number on that field

I have an array of object and each object is for example :
const myArr=[{name:"john",id:1}{name:"john",id:2}{name:"mary",id:3}]
for the first 2 element for the property "name" I have the name "john" that is duplicate.
How can I modify the rendered names like that:
const myArr=[{name:"john (1 of 2)",id:1}{name:"john (2 of 2)",id:2}{name:"mary",id:3}]
Thanks in advance!
Reduce the input array into a map by name (i.e. group by name property), and map the array of values to the result array. If the group array has more than 1 element in it then sub-map the group to include the numbering. Flatten the overall result.
const myArr = [
{ name: "john", id: 1 },
{ name: "john", id: 2 },
{ name: "mary", id: 3 }
];
const res = Object.values(
myArr.reduce((groups, current) => {
if (!groups[current.name]) {
groups[current.name] = [];
}
groups[current.name].push(current);
return groups;
}, {})
).flatMap((value) => {
if (value.length > 1) {
return value.map((current, i, arr) => ({
...current,
name: `${current.name} (${i + 1} of ${arr.length})`
}));
}
return value;
});
console.log(res);
You can do use reduce(), filter(), and flat() and do this:
const myArr = [
{name:"john", id:1},
{name:"john", id:2},
{name:"mary", id:3}
]
const res = Object.values(myArr.reduce((acc, curr) => {
const total = myArr.filter(({ name }) => name === curr.name).length;
if(!acc[curr.name]) {
acc[curr.name] = [
{...curr}
]
} else {
const currentSize = acc[curr.name].length;
if(currentSize === 1) {
acc[curr.name][0].name = `${acc[curr.name][0].name} (1 of ${total})`
}
acc[curr.name].push({
...curr,
name: `${curr.name} (${currentSize + 1} of ${total})`
})
}
return acc;
}, {})).flat();
console.log(res);
const myArr = [{name:"john",id:1}, {name:"john",id:2}, {name:"mary",id:3}];
const namesArray = myArr.map(elem => elem.name);
const namesTraversed = [];
let currentCountOfName = 1;
let len = 0;
myArr.forEach(elem => {
len = namesArray.filter(name => name === elem.name).length;
if (len > 1) {
if (namesTraversed.includes(elem.name)) {
namesTraversed.push(elem.name);
currentCountOfName = namesTraversed.filter(name => name === elem.name).length;
elem.name = `${elem.name} (${currentCountOfName} of ${len})`;
} else {
namesTraversed.push(elem.name);
currentCountOfName = 1;
elem.name = `${elem.name} (${currentCountOfName} of ${len})`;
}
}
});
console.log(myArr);
Check if this helps you
const myArr = [{
name: "john",
id: 1
}, {
name: "john",
id: 2
}, {
name: "mary",
id: 3
}]
// to keep a track of current copy index
let nameHash = {}
const newMyArr = myArr.map(ele => {
const noOccurence = myArr.filter(obj => obj.name ===ele.name).length;
if(noOccurence > 1){
// if there are multiple occurences get the current index. If undefined take 1 as first copy index.
let currentIndex = nameHash[ele.name] || 1;
const newObj = {
name: `${ele.name} (${currentIndex} of ${noOccurence})`,
id: ele.id
}
nameHash[ele.name] = currentIndex+ 1;
return newObj;
}
return ele;
})
console.log(newMyArr);

Mapping a compounded array from objects with JavaScript [duplicate]

This question already has answers here:
Create an array with same element repeated multiple times
(25 answers)
Closed 3 years ago.
I am trying to create a simngle array of multiple values obtained from an input array of objects. Ideally I'd like to use "ES5" features to do this.
I need to transform an object such as this:
{
image: "bat",
count: 5
}
into this array [bat, bat, bat, bat, bat]
I think the code is the fastest way to explain:
//game object state
var gameObj = {
objects: [
{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
}
function gameObjectArrayBuilder(obj) {
var resultArr = [];
resultArr = obj.map(buildArr);
function buildArr(prop) {
var image = prop.image;
var count = prop.count;
while (prop.count > 0) {
prop.count--
return prop.image
}
}
return resultArr;
}
gameObj.imagesArr = gameObjectArrayBuilder(gameObj.objects);
//View result
var html = document.querySelector("#content");
html.innerHTML = gameObj.imagesArr;
console.log(gameObj.imagesArr)
//Result should be
//[bat, bat, bat, bat, bat, spider, spider, spider, spider, frankie, frankie, frankie]
<div id="content">
</div>
To obtain an array of duplicate string values (ie "bat"), where the length of the array corresponds to a supplied variable (ie "obj.count"), you could do the following:
const obj = { image: "bat", count: 5 };
const arr = Array.from({ length : obj.count }, () => (obj.image));
console.log(arr);
You could then build upon this to populate an array based on multiple input objects like this:
var gameObj = {
objects: [{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
}
// Use reduce to obtain compounded array from all input objects
gameObj.imagesArr = gameObj.objects.reduce((acc, obj) => {
// Concat acc array with resulting array from current obj
return acc.concat(Array.from({ length: obj.count }, () => (obj.image)))
}, []);
console.log(gameObj.imagesArr);
You can use a combination of new Array and the reduce method for your case:
var gameObj = {
objects: [{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
};
function repeatImages(acc, item, idx) {
if (!(acc instanceof Array)) {
acc = [];
}
return acc.concat(new Array(item.count).fill(item.image));
}
gameObj.imagesArr = gameObj.objects.reduce(repeatImages, []);
console.log(gameObj.imagesArr);
Solution For your example
//game object state
var gameObj = {
objects: [
{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
}
function gameObjectArrayBuilder(obj) {
var resultArr = [];
obj.forEach((ele)=>{resultArr.push(...buildArr(ele))});
function buildArr(prop) {
var image = prop.image;
var count = prop.count;
var temp = []
while (prop.count > 0) {
prop.count--
temp.push(prop.image)
}
return temp
}
return resultArr;
}
gameObj.imagesArr = gameObjectArrayBuilder(gameObj.objects);
//View result
var html = document.querySelector("#content");
html.innerHTML = gameObj.imagesArr;
console.log(gameObj.imagesArr)
//Result should be
//[bat, bat, bat, bat, bat, spider, spider, spider, spider, frankie, frankie, frankie]
<div id="content">
</div>
You could take the count and loop this count as long as it has a truthy value.
Instead of mapping (Array#map), I would reduce (Array#reduce) the array, because the result set is an array with a count of items. In new user agents, Array#flatMap would work, but here an accumulator is necessary for getting all same items.
function gameObjectArrayBuilder(array) {
function buildArr(accumulator, object) {
var count = object.count;
while (count--) accumulator.push(object.image);
return accumulator;
}
return array.reduce(buildArr, []);
}
var gameObj = { objects: [{ image: "bat", count: 5 }, { image: "spider", count: 4 }, { image: "frankie", count: 3 }], imagesArr: [] };
gameObj.imagesArr = gameObjectArrayBuilder(gameObj.objects);
var html = document.querySelector("#content");
html.innerHTML = gameObj.imagesArr;
console.log(gameObj.imagesArr)
<div id="content"></div>
You can just use a for cycle to add items to a tmp array, then return it:
//game object state
var gameObj = {
objects: [
{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
}
function gameObjectArrayBuilder(obj)
{
var resultArr = [];
resultArr = obj.map(buildArr);
function buildArr(prop)
{
var image = prop.image;
var count = prop.count;
// USE A TMP ARRAY
let tmpArr = []
for (let j=0; j<count; j++) {
tmpArr.push(prop.image);
}
return tmpArr
}
return resultArr;
}
gameObj.imagesArr = gameObjectArrayBuilder(gameObj.objects);
//View result
var html = document.querySelector("#content");
html.innerHTML = gameObj.imagesArr;
<div id="content">
</div>
You can use flatMap to produce the same result with less number of codes.
//game object state
var gameObj = {
objects: [
{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
}
function gameObjectArrayBuilder(obj) {
var resultArr = obj.flatMap( f=> {
return [...Array(f.count).fill(f.image)]
});
return resultArr;
}
gameObj.imagesArr = gameObjectArrayBuilder(gameObj.objects);
//View result
var html = document.querySelector("#content");
html.innerHTML = gameObj.imagesArr;
console.log(gameObj.imagesArr)
//Result should be
//[bat, bat, bat, bat, bat, spider, spider, spider, spider, frankie, frankie, frankie]
<div id="content">
</div>

Array compare with for each performance issue

I have two arrays with array of objects as follows and one array will have more than 10k records and other have below 100 records
let bigArray = [{id:1, name:"Raj", level:0}, {id:2, name:"sushama", level:2}, {id:3, name:"Sushant", level:0}, {id:4, name:"Bhaskar", level:2},....upto 30k records]
let smallArray = [{id:2, name:"sushama"}, {id:3, name:"Sushant"}....upto 100 records]
I want to find where in the index of bigArray in which the object from smallArray resides and add to another array say indexArray I tried below
let indexArray = [];
bigArray.forEach((element, i) => {
smallArray.forEach(ele => {
if (element.name == ele.name && element.id == ele.id) {
indexArray.push(i); return;
}
});
});
But it takes time. What would be the fastest approach?
You can turn your O(N^2) approach into an O(N) approach by reducing the bigArray into an object indexed by a key made up from the name and id. Join the name and id by a character that isn't contained in either, such as _:
const indexArray = [];
const bigArrayIndiciesByNameAndId = bigArray.reduce((a, { name, id }, i) => {
a[name + '_' + id] = i;
return a;
}, {});
smallArray.forEach(ele => {
const keyToFind = ele.name + '_' + ele.id;
const foundIndex = bigArrayIndiciesByNameAndId[keyToFind];
if (foundIndex) {
indexArray.push(foundIndex);
}
});
You could take a Map and map the found indices.
const getKey = ({ id, name }) => [id, name].join('|');
let bigArray = [{ id: 1, name: "Raj", level: 0 }, { id: 2, name: "sushama", level: 2 }, { id: 3, name: "Sushant", level: 0 }, { id: 4, name: "Bhaskar", level: 2 }],
smallArray = [{ id: 2, name: "sushama" }, { id: 3, name: "Sushant" }],
map = new Map(bigArray.map((o, i) => [getKey(o), i]))
indexArray = smallArray.map((o) => map.get(getKey(o)));
console.log(indexArray);
return will not "break" the forEach loop. A forEach can't be stopped. The forEach callback function will be called one time per items always. When you find the element, continue running the forEach loop is a waste of resouces.
You should use for instead:
let indexArray = [];
bigArray.forEach((element, i) => {
for (var ii = 0; ii < smallArray.length; ii++) {
var ele = smallArray[ii];
if (element.name == ele.name && element.id == ele.id) {
indexArray.push(i);
break; // This will break the "for" loop as we found the item
}
}
});
TIP: Always have a good indentation in your code. Actually your code is really bad indented to identify code blocks at first sight. I fixed it in this example.

Get all possible options for a matrix in javascript

I have an 'item' object in JavaScript, and the item can have settings like
color, size, etc.
I need to get all possible combinations in an array.
So lets say we have an item that looks like this:
var newItem = {
name: 'new item',
Settings: [
{name: 'color', values: ['green', 'blue', 'red']},
{name: 'size', values: ['15', '18', '22']},
{name: 'gender',values: ['male', 'female']}
]
};
I need to somehow get this:
[
[{SettingName:'color',value:'green'},{SettingName:'size',value:'15'},{SettingName:'gender',value:'male'}],
[{SettingName:'color',value:'blue'},{SettingName:'size',value:'15'},{SettingName:'gender',value:'male'}],
[{SettingName:'color',value:'red'},{SettingName:'size',value:'15'},{SettingName:'gender',value:'male'}],
[{SettingName:'color',value:'green'},{SettingName:'size',value:'18'},{SettingName:'gender',value:'male'}],
[{SettingName:'color',value:'blue'},{SettingName:'size',value:'18'},{SettingName:'gender',value:'male'}],
[{SettingName:'color',value:'red'},{SettingName:'size',value:'18'},{SettingName:'gender',value:'male'}],
[{SettingName:'color',value:'green'},{SettingName:'size',value:'22'},{SettingName:'gender',value:'male'}],
[{SettingName:'color',value:'blue'},{SettingName:'size',value:'22'},{SettingName:'gender',value:'male'}],
[{SettingName:'color',value:'red'},{SettingName:'size',value:'22'},{SettingName:'gender',value:'male'}],
[{SettingName:'color',value:'green'},{SettingName:'size',value:'15'},{SettingName:'gender',value:'female'}],
[{SettingName:'color',value:'blue'},{SettingName:'size',value:'15'},{SettingName:'gender',value:'female'}],
[{SettingName:'color',value:'red'},{SettingName:'size',value:'15'},{SettingName:'gender',value:'female'}],
[{SettingName:'color',value:'green'},{SettingName:'size',value:'18'},{SettingName:'gender',value:'female'}],
[{SettingName:'color',value:'blue'},{SettingName:'size',value:'18'},{SettingName:'gender',value:'female'}],
[{SettingName:'color',value:'red'},{SettingName:'size',value:'18'},{SettingName:'gender',value:'female'}],
[{SettingName:'color',value:'green'},{SettingName:'size',value:'22'},{SettingName:'gender',value:'female'}],
[{SettingName:'color',value:'blue'},{SettingName:'size',value:'22'},{SettingName:'gender',value:'female'}],
[{SettingName:'color',value:'red'},{SettingName:'size',value:'22'},{SettingName:'gender',value:'female'}]
]
This can be a good interview question.
See JS Bin for running example.
getAllPermutations(newItem);
function getAllPermutations(item) {
var permutations = [];
getAllPermutations0(item, permutations, []);
console.log(permutations);
}
function getAllPermutations0(item, permutations, array) {
if (array && array.length === item.Settings.length) {
permutations.push(array.slice()); // The slice clone the array
return;
}
var index = array.length;
var setting = item.Settings[index];
for (var i = 0; i < setting.values.length; i++) {
if (index === 0)
array = [];
var currValue = setting.values[i];
array.push({
SettingName: setting.name,
value: currValue
});
getAllPermutations0(item, permutations, array);
array.pop(); // pop the old one first
}
}
Here is a none recursive solution. It takes an empty or existing settings "matrix" and a values array, and return a new matrix as a combination of existing matrix content cloned for each new value, appended with pairs of new value setting items.
[A] -> [1,2] gives [A][1][A][2]
[A][1][A][2] -> [X,Y] gives [A][1][X][A][2][Y][A][2][X][A][1][Y]
and so on
function processSettings(settings, name, values) {
if (settings.length == 0) {
values.forEach(function(value) {
settings.push( [{ SettingName: name, value: value }] )
})
} else {
var oldSettings = JSON.parse(JSON.stringify(settings)), settings = [], temp, i = 0
for (i; i<values.length; i++) {
temp = JSON.parse(JSON.stringify(oldSettings))
temp.forEach(function(setting) {
setting.push( { SettingName: name, value: values[i] } )
settings.push(setting)
})
}
}
return settings
}
You can now create the desired settings literal this way :
var settings = []
for (var i=0; i<newItem.Settings.length; i++) {
var item = newItem.Settings[i]
settings = processSettings(settings, item.name, item.values)
}
demo -> http://jsfiddle.net/b4ck98mf/
The above produces this :
[
[{"SettingName":"color","value":"green"},{"SettingName":"size","value":"15"},{"SettingName":"gender","value":"male"}],
[{"SettingName":"color","value":"blue"},{"SettingName":"size","value":"15"},{"SettingName":"gender","value":"male"}],
[{"SettingName":"color","value":"red"},{"SettingName":"size","value":"15"},{"SettingName":"gender","value":"male"}],
[{"SettingName":"color","value":"green"},{"SettingName":"size","value":"18"},{"SettingName":"gender","value":"male"}],
[{"SettingName":"color","value":"blue"},{"SettingName":"size","value":"18"},{"SettingName":"gender","value":"male"}],
[{"SettingName":"color","value":"red"},{"SettingName":"size","value":"18"},{"SettingName":"gender","value":"male"}],
[{"SettingName":"color","value":"green"},{"SettingName":"size","value":"22"},{"SettingName":"gender","value":"male"}],
[{"SettingName":"color","value":"blue"},{"SettingName":"size","value":"22"},{"SettingName":"gender","value":"male"}],
[{"SettingName":"color","value":"red"},{"SettingName":"size","value":"22"},{"SettingName":"gender","value":"male"}],
[{"SettingName":"color","value":"green"},{"SettingName":"size","value":"15"},{"SettingName":"gender","value":"female"}],
[{"SettingName":"color","value":"blue"},{"SettingName":"size","value":"15"},{"SettingName":"gender","value":"female"}],
[{"SettingName":"color","value":"red"},{"SettingName":"size","value":"15"},{"SettingName":"gender","value":"female"}],
[{"SettingName":"color","value":"green"},{"SettingName":"size","value":"18"},{"SettingName":"gender","value":"female"}],
[{"SettingName":"color","value":"blue"},{"SettingName":"size","value":"18"},{"SettingName":"gender","value":"female"}],
[{"SettingName":"color","value":"red"},{"SettingName":"size","value":"18"},{"SettingName":"gender","value":"female"}],
[{"SettingName":"color","value":"green"},{"SettingName":"size","value":"22"},{"SettingName":"gender","value":"female"}],
[{"SettingName":"color","value":"blue"},{"SettingName":"size","value":"22"},{"SettingName":"gender","value":"female"}],
[{"SettingName":"color","value":"red"},{"SettingName":"size","value":"22"},{"SettingName":"gender","value":"female"}]
]
You can use Array.prototype.map(), for loop, while loop, Array.prototype.concat(). Iterate gender values; select each of color, size value in succession beginning at index 0 of either; iterating the furthest adjacent array from current gender, increment the index of the closest adjacent array; merge the resulting two gender arrays to form a single array containing all combinations of gender, color, size
var colors = newItem.Settings[0].values;
var sizes = newItem.Settings[1].values;
var gen = newItem.Settings[2].values;
var i = sizes.length;
var res = [].concat.apply([], gen.map(function(value, key) {
var next = -1;
var arr = [];
for (var curr = 0; curr < i; curr++) {
while (next < i - 1) {
arr.push([{
SettingName: "gender",
value: value
}, {
SettingName: "size",
value: sizes[curr]
}, {
SettingName: "color",
value: colors[++next]
}])
}
next = -1;
}
return arr
}))
var newItem = {
"name": "new item",
"Settings": [{
"name": "color",
"values": [
"green",
"blue",
"red"
]
}, {
"name": "size",
"values": [
"15",
"18",
"22"
]
}, {
"name": "gender",
"values": [
"male",
"female"
]
}]
}
var colors = newItem.Settings[0].values;
var sizes = newItem.Settings[1].values;
var gen = newItem.Settings[2].values;
var i = sizes.length;
var res = [].concat.apply([], gen.map(function(value, key) {
var next = -1;
var arr = [];
for (var curr = 0; curr < i; curr++) {
while (next < i - 1) {
arr.push([{
SettingName: "gender",
value: value
}, {
SettingName: "size",
value: sizes[curr]
}, {
SettingName: "color",
value: colors[++next]
}])
}
next = -1;
}
return arr
}))
document.querySelector("pre").textContent = JSON.stringify(res, null, 2)
<pre></pre>
plnkr http://plnkr.co/edit/C2fOJpfwOrlBwHLQ2izh?p=preview
An approach using Array.prototype.reduce(), Array.prototype.sort(), Object.keys(), for loop, while loop
var newItem = {
name: 'new item',
Settings: [
{
name: 'color',
values: ['green', 'blue', 'red']
},
{
name: 'size',
values: ['15', '18', '22']
},
{
name: 'gender',
values: ['male', 'female']
}
]
};
var props = ["SettingName", "value"];
var settings = newItem.Settings;
function p(settings, props) {
var data = settings.reduce(function(res, setting, index) {
var name = setting.name;
var obj = {};
obj[name] = setting.values;
res.push(obj);
return res.length < index ? res : res.sort(function(a, b) {
return a[Object.keys(a)[0]].length - b[Object.keys(b)[0]].length
})
}, []);
var key = data.splice(0, 1)[0];
return [].concat.apply([], key[Object.keys(key)].map(function(value, index) {
return data.reduce(function(v, k) {
var keys = [v, k].map(function(obj) {
return Object.keys(obj)[0]
});
var i = Math.max.apply(Math, [v[keys[0]].length, k[keys[1]].length]);
var next = -1;
var arr = [];
for (var curr = 0; curr < i; curr++) {
while (next < i - 1) {
var a = {};
a[props[0]] = keys[0];
a[props[1]] = v[keys[0]][++next];
var b = {};
b[props[0]] = keys[1];
b[props[1]] = k[keys[1]][next];
var c = {};
c[props[0]] = Object.keys(key)[0];
c[props[1]] = value;
arr.push([a, b, c]);
};
next = -1;
}
return arr
});
}));
}
document.querySelector("pre").textContent = JSON.stringify(
p(settings, props), null, 2
);
<pre></pre>

Categories

Resources