I need to be able to convert an array into a new array containing multiple objects. For example, if I have this array:
["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"]
I want to be able to convert it into this:
[{
"name": "Tom",
"id": "48688"
}, {
"name": "Bob"
"id": "91282"
}]
It is common to see a zip function taking a key k and a value v and create an object with them:
const zip =
(k, v) =>
({[k]: v});
zip("name", "Tom");
//=> {name: "Tom"}
If both key and value are in an array you can spread it in a zip call like that zip(...arr). Or you can modify the signature a little bit:
const zip =
([k, v]) =>
({[k]: v});
zip(["name", "Tom"]);
//=> {name: "Tom"}
If the array contains multiple pairs of keys and values then we can design a recursive version of zip:
const Nil = Symbol();
const zip =
([k = Nil, v = Nil, ...xs], o = {}) =>
k === Nil && v === Nil
? o
: zip(xs, (o[k] = v, o));
zip(["name", "Tom", "id", "48688"]);
//=> {name: "Tom", id: "48688"}
We can now think about slicing your array into chunks of equal number of pairs and apply zip to each chunk.
First let's write a slices function that will cut an array into slices of n elements:
const slices =
(xs, n, ys = []) =>
xs.length === 0
? ys
: slices(xs.slice(n), n, (ys.push(xs.slice(0, n)), ys));
slices(["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"], 4);
//=> [["name", "Tom", "id", "48688"],["name", "Bob", "id", "91282"]]
We can now apply zip to each chunk:
slices(["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"], 4)
.map(chunk => zip(chunk));
//=> [{name: "Tom", id: "48688"},{name: "Bob", id: "91282"}]
const Nil = Symbol();
const zip =
([k = Nil, v = Nil, ...xs], o = {}) =>
k === Nil && v === Nil
? o
: zip(xs, (o[k] = v, o));
const slices =
(xs, n, ys = []) =>
xs.length === 0
? ys
: slices(xs.slice(n), n, (ys.push(xs.slice(0, n)), ys));
console.log(
slices(["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"], 4)
.map(chunk => zip(chunk))
);
Use a for loop that increments its iteration by 4, like so:
let results = [];
for(let i = 0; i < array.length; i += 4) { // increment i by 4 to get to the start of the next object data
results.push({
id: array[i + 3], // array[i + 0] is the string "name", array[i + 1] is the name,
name: array[i + 1] // array[i + 2] is the string "id" and array[i + 3] is the id
});
}
Demo:
let array = ["name", "Tom", "id", "48688", "name", "Bob", "id", "91282", "name", "Ibrahim", "id", "7"];
let results = [];
for(let i = 0; i < array.length; i += 4) {
results.push({
id: array[i + 3],
name: array[i + 1]
});
}
console.log(results);
I see questions like this very, very often, so I made a little converter that accomplishes this particular goal:
// input
var inputArray = ["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"]
var sizeOfObjects = 2; // amount of entries per object
// function
function convert(array, size) {
var newArray = [] //set up an array
var res3 = array.reduce((acc, item, index) => {
if (index % 2 == 0) { // if the index is even:
acc[`${item}`] = array[index+1]; // add entry to array
}
if (Object.keys(acc).length == size) { // if the desired size has been reached:
newArray.push(acc); // push the object into the array
acc = {}; // reset the object
}
return acc; // preserve accumulator so it doesn't get forgotten
}, {}); // initial value of reducer is an empty object
return newArray; //return the array
}
console.log(convert(inputArray, sizeOfObjects));
Hopefully this helps people who are looking for an answer for this kind of question.
If you're looking to just create a single object, look at this other question/answer: Create object from array
We can use % operator to decide whether we find an object to insert into array or not:
const data = ["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"];
makeObjectArray = arr => {
const result = [], temp = [];
arr.forEach((a, i)=>{
if (i % 2 == 0)
temp.push({ [arr[i]]: arr[i + 1]})
if (i % 3 == 0 && i != 0) {
result.push(Object.assign({}, ...temp));
temp.length = 0;
}
})
return result;
}
console.log(makeObjectArray(data))
you can break the array into smaller chunks of the desired size with a helper function like:
function chunk(to_chunk, chunk_size) {
var output = [];
if(to_chunk.length > chunk_size) {
output.push(to_chunk.slice(0, chunk_size));
output.push(chunk(to_chunk.slice(chunk_size)));
return output;
} else {
return to_chunk;
}
}
Then you can map the result with other function to return your desired object:
var final = chunk(seed, 4).map((x) => myObject(x));
function myObject(seed) {
var output = {};
output[seed[0]] = seed[1];
output[seed[2]] = seed[3];
return output;
}
I think this approach is nice in terms of readability, putting all together you have:
var seed = ["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"];
var final = chunk(seed, 4).map((x) => myObject(x));
console.log(final);
function chunk(to_chunk, chunk_size)
{
var output = [];
if(to_chunk.length > chunk_size) {
output.push(to_chunk.slice(0, chunk_size));
output.push(chunk(to_chunk.slice(chunk_size)));
return output;
} else {
return to_chunk;
}
}
function myObject(seed)
{
var output = {};
output[seed[0]] = seed[1];
output[seed[2]] = seed[3];
return output;
}
You could take a dynamic approach by using an object for keeping track of the target index for same named keys.
const
getArray = data => {
let indices = {},
result = [],
i = 0;
while (i < data.length) {
const [key, value] = data.slice(i, i += 2);
indices[key] ??= 0;
(result[indices[key]++] ??= {})[key] = value;
}
return result;
},
data1 = ["name", "Tom", "id", "48688", "name", "Bob", "id", "91282"],
data2 = ["name", "Tom", "id", "48688", "color", "green", "name", "Bob", "id", "91282", "color", "red"];
console.log(getArray(data1));
console.log(getArray(data2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I am stuck to solve this problem.
Convert an array below
var input = [
'animal/mammal/dog',
'animal/mammal/cat/tiger',
'animal/mammal/cat/lion',
'animal/mammal/elephant',
'animal/reptile',
'plant/sunflower'
]
to json Object
var expectedResult = {
"animal": {
"mammal": {
"dog": true,
"cat": {
"tiger": true,
"lion": true
},
"elephant": true
},
"reptile": true
},
"plant": {
"sunflower": true
}
}
Which data structure and algorithm can I apply for it?
Thanks
You need to first split each element to convert to array
using reverse reduce method you can convert them to object.
And your last step is merge this objects.
Lodash.js merge method is an one way to merge them.
var input = ['animal/mammal/dog','animal/mammal/cat/tiger','animal/mammal/cat/lion', 'animal/mammal/elephant','animal/reptile', 'plant/sunflower']
var finalbyLodash={}
input.forEach(x=>{
const keys = x.split("/");
const result = keys.reverse().reduce((res, key) => ({[key]: res}), true);
finalbyLodash = _.merge({}, finalbyLodash, result);
});
console.log(finalbyLodash);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
To make the process more understandable, break the problem down into pieces.
The first step is convert each string into something we can use, converting this:
"animal/mammal/dog"
into this:
[ "animal", "mammal", "dog" ]
That's an array of property names needed to build the final object.
Two functions will accomplish this for you, String.prototype.split() to split the string into an array, and Array.prototype.map() to transform each of the array elements:
let splitIntoNames = input.map(str => str.split('/'));
The intermediate result is this:
[
[ "animal", "mammal", "dog" ],
[ "animal", "mammal", "cat", "tiger" ],
[ "animal", "mammal", "cat", "lion" ],
[ "animal", "mammal", "elephant" ],
[ "animal", "reptile" ],
[ "plant", "sunflower" ]
]
Next step is to iterate over each array, using Array.prototype.forEach() to add properties to the object. You could add properties to the object with a for loop, but let's do that with a recursive function addName():
function addName(element, list, index) {
if (index >= list.length) {
return;
}
let name = list[index];
let isEndOfList = index === list.length - 1;
element[name] = element[name] || (isEndOfList ? true : {});
addName(element[name], list, index + 1);
}
let result = {};
splitIntoNames.forEach((list) => {
addName(result, list, 0);
});
The result:
result: {
"animal": {
"mammal": {
"dog": true,
"cat": {
"tiger": true,
"lion": true
},
"elephant": true
},
"reptile": true
},
"plant": {
"sunflower": true
}
}
const input = [
"animal/mammal/dog",
"animal/mammal/cat/tiger",
"animal/mammal/cat/lion",
"animal/mammal/elephant",
"animal/reptile",
"plant/sunflower",
];
let splitIntoNames = input.map((str) => str.split("/"));
console.log("splitIntoNames:", JSON.stringify(splitIntoNames, null, 2));
function addName(element, list, index) {
if (index >= list.length) {
return;
}
let name = list[index];
let isEndOfList = index === list.length - 1;
element[name] = element[name] || (isEndOfList ? true : {});
addName(element[name], list, index + 1);
}
let result = {};
splitIntoNames.forEach((list) => {
addName(result, list, 0);
});
console.log("result:", JSON.stringify(result, null, 2));
You can create a function that will slice every element from the array by "/" than you put the results into a variable and than just mount the Json. I mean something like that below:
window.onload = function() {
var expectedResult;
var input = [
'animal/mammal/dog',
'animal/mammal/cat/tiger',
'animal/mammal/cat/lion',
'animal/mammal/elephant',
'animal/reptile',
'plant/sunflower'
]
input.forEach(element => {
var data = element.split('/');
var dog = data[2] === 'dog' ? true : false
var tiger = data[2] === 'cat' && data[3] === 'tiger' ? true : false
var lion = data[2] === 'cat' && data[3] === 'lion' ? true : false
expectedResult = {
data[0]: {
data[1]: {
"dog": dog,
"cat": {
"tiger": tiger,
"lion": lion
}
}
}
}
})
}
Late to the party, here is my try. I'm implmenting recursive approach:
var input = ['animal/mammal/dog', 'animal/mammal/cat/tiger', 'animal/mammal/cat/lion', 'animal/mammal/elephant', 'animal/reptile', 'plant/sunflower'];
result = (buildObj = (array, Obj = {}) => {
array.forEach((val) => {
keys = val.split('/');
(nestedFn = (object) => {
outKey = keys.shift();
object[outKey] = object[outKey] || {};
if (keys.length == 0) object[outKey] = true;
if (keys.length > 0) nestedFn(object[outKey]);
})(Obj)
})
return Obj;
})(input);
console.log(result);
I try with array reduce, hope it help
let input = [
"animal/mammal/dog",
"animal/mammal/cat/tiger",
"animal/mammal/cat/lion",
"animal/elephant",
"animal/reptile",
"plant/sunflower",
];
let convertInput = (i = []) =>
i.reduce((prev, currItem = "") => {
let pointer = prev;
currItem.split("/").reduce((prevPre, currPre, preIdx, arrPre) => {
if (!pointer[currPre]) {
pointer[currPre] = preIdx === arrPre.length - 1 ? true : {};
}
pointer = pointer[currPre];
}, {});
return prev;
}, {});
console.log(JSON.stringify(convertInput(input), null, 4));
I have an array like,
0: "City1"
1: {name="sds", age="asd",....}
2: {name="sweds", age="accxsd",....}
3: {name="sdqws", age="asssd",....}
4: "City2"
... and many more
So I need to get the elements between index[0] and index[4],
Am able to check the string and object using typeof
for(i=0; i<=arr.length; i++){
if(typeof arr[i] == 'string'){
... // need to find next element eith type string
}
}
Is there a way to find the next element in an array whose value is string, so I can get elements between them.
You can use this alternative using the function reduce.
This approach builds an object grouping the objects into an array with the found string value.
var array = [ "City1", {name:"sds", age:"asd"}, {name:"sweds", age:"accxsd"}, {name:"sdqws", age:"asssd"}, "City2", {name:"sds2", age:"asd2"}, {name:"sweds2", age:"accxsd2"}, {name:"sdqws2", age:"asssd2"}];
var result = array.reduce((a, c) => {
if (typeof c === 'string') {
a[c] = [];
a.current = c;
} else if (a.current !== "") {
a[a.current].push(c);
}
return a;
}, {current: ""});
delete result.current;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
How can I adjust to a particular String value, like if my input id 'city3', I need to get all elements between 'city3' and its next string value
The above approach groups the elements by the previously found string element, so you can directly access the desired target City3
var array = [ "City1", {name:"sds", age:"asd"}, {name:"sweds", age:"accxsd"}, {name:"sdqws", age:"asssd"}, "City3", {name:"sds3", age:"asd3"}, {name:"sweds3", age:"accxsd3"}, {name:"sdqws3", age:"asssd3"}, "City4", {name:"sds4", age:"asd4"}, {name:"sweds4", age:"accxsd4"}, {name:"sdqws4", age:"asssd"}];
var result = array.reduce((a, c) => {
if (typeof c === 'string') {
a[c] = [];
a.current = c;
} else if (a.current !== "") {
a[a.current].push(c);
}
return a;
}, {
current: ""
});
delete result.current;
var target = "City3";
// Now you have a direct access to find the desired target.
console.log(result[target]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can just filter your array:
var arr = [
"City1",
{name:"sds", age:"asd"},
{name:"sweds", age:"accxsd"},
{name:"sdqws", age:"asssd"},
"City2"
];
var res = arr.filter(e => typeof e !== 'string');
console.log(res);
EDIT: if you want result from a specified start string, it should be:
var arr = [
"City1",
{name:"sds1", age:"asd"},
{name:"sweds1", age:"accxsd"},
{name:"sdqws1", age:"asssd"},
"City2",
{name:"sds2", age:"asd"},
{name:"sweds2", age:"accxsd"},
{name:"sdqws2", age:"asssd"},
"City3"
];
var str = 'City2';
var start = arr.indexOf(str);
var end = arr.findIndex((s, i) => typeof s === 'string' && i > start);
var res = arr.filter((e, i) => i > start && i < end);
console.log(res);
You could take a flag for filtering.
If a string is found switch the filter flag by checking the value with the wanted group 'City3'.
var array = ["City1", { name: "city1", age: 22 }, { name: "city1", age: 23 }, "City2", { name: "city2", age: 22 }, { name: "city2", age: 23 }, "City3", { name: "city3", age: 21 }, { name: "city3", age: 22 }, { name: "city3", age: 23 }, "City4", { name: "city4", age: 23 }, "City5"],
group = 'City3';
result = array.filter(
(f => v => typeof v === 'string' ? (f = v === group, false) : f)(false)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using a traditional for...loop you can use continue in the loop to progress to the next index if your condition is a match:
const data = [
"City1",
{ name:"sds", age: "asd" },
{ name: "sweds", age: "accxsd" },
{ name: "sdqws", age: "asssd" },
"City2"
]
for (let i = 0; i < data.length; i++) {
if (typeof data[i] === 'string') continue;
console.log(data[i].name)
}
I'm new to StackOverflow and I know this post might possibly be a duplicate of another so please spare me with all the downvotes and if you think there's an answer to my question out there, please post it and I'll delete this question. Thanks for understanding.
var array1 = ["name", "title", "desc"]
var array2 = [["name1", "name2"], ["title1", "title2"],["desc1", "desc2"]]
How will I turn these into:
[
{name: "name1", title: "title1", desc: "desc1"},
{name: "name2", title: "title2", desc: "desc2"}
]
You can use Array#map, Object.assign (with spread syntax) and the ES6 computed property syntax to achieve that:
const array1 = ["name", "title", "desc"],
array2 = [["name1", "name2"], ["title1", "title2"],["desc1", "desc2"]];
const result = array2[0].map( (_, j) =>
Object.assign(...array1.map( (key, i) => ({ [key]: array2[i][j] }) ))
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const result = [];
for(const [index, key] of array1.entries()){
for(const [userindex, value] of array2[index].entries()){
if(!result[userindex])
result[userindex] = {};
result[userindex][key] = value;
}
}
You might go over every key and the values related to the key and assign every key/value pair to the resulting object at the position of the value.
You could reduce the given values array by using the keys as key and the value for new objects.
var keys = ["name", "title", "desc"],
values = [["name1", "name2"], ["title1", "title2"],["desc1", "desc2"]],
objects = values.reduce((r, a, i) => {
a.forEach((v, j) => Object.assign(r[j] = r[j] || {}, { [keys[i]]: v }));
return r;
}, []);
console.log(objects);
You can use this way also:
var array1 = ["name", "title", "desc"];
var array2 = [["name1", "name2"], ["title1", "title2"],["desc1", "desc2"]];
var res = [];
for(var i=0; i<array2[0].length; i++){
var obj = {};
for(var j=0; j<array1.length; j++){
var key = array1[j];
var value = array2[j][i];
obj[key] = value;
}
res.push(obj);
}
console.log(res);
I have an array of objects like below:
var array =
[
{"name":"abc","age":20}
{"name":"abc","age":20}
{"name":"abc","age":20}
{"name":"xyz","age":21}
{"name":"xyz","age":21}
]
I want to count the number of occurrences of distinct values like:
[3,2]
Assuming abc has 3 occurrences and xyz has 2 occurrences.
I am doing it in reactjs. I am able to get distinct values like [abc,xyz] using this answer.
ES6 syntax is preferred.
You'll need to know to which name a count belongs, so I propose not to output an array that gives you no clue about that, but an object keyed by names and with as value the corresponding count:
var result = array.reduce( (acc, o) => (acc[o.name] = (acc[o.name] || 0)+1, acc), {} );
var array =
[
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"xyz","age":21},
{"name":"xyz","age":21}
];
var result = array.reduce( (acc, o) => (acc[o.name] = (acc[o.name] || 0)+1, acc), {} );
console.log(result);
Map/Reduce to the rescue:
const frequency = array
.map(({ name }) => name)
.reduce((names, name) => {
const count = names[name] || 0;
names[name] = count + 1;
return names;
}, {});
// frequency: { abc: 3, xyz: 2 }
You can use forEach/map to iterate the array and store the count in another variable, Check this:
var array = [
{"name" : "abc", "age" : 20},
{"name" : "abc", "age" : 20},
{"name" : "abc", "age" : 20},
{"name" : "xyz", "age" : 21},
{"name" : "xyz", "age" : 21},
];
let b = {};
array.forEach(el => {
b[el.name] = (b[el.name] || 0) + 1;
})
console.log(b);
Still if anyone looking for distinct counts stored in an array
var result = array.reduce( (acc, o) => (acc[o.name] = (acc[o.name] || 0)+1, acc), {} );
result = Object.values(result); // returns an array of values from the object
// result will be [3,2]
This is one way to do it:
var array =
[
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"xyz","age":21},
{"name":"xyz","age":21}
]
let yy = {}
array.map( el => {
yy[el.name] = (yy[el.name] || 0) + 1
})
console.log(yy)
And this is another way:
var array =
[
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"abc","age":20},
{"name":"xyz","age":21},
{"name":"xyz","age":21}
]
let yy = {}
array.map( el => {
if (yy[el.name] === undefined || yy[el.name] === 0) {
yy[el.name] = 1
} else {
yy[el.name] = yy[el.name] + 1
}
})
console.log(yy)
Quick answer : new Set(array).size.
Explanation (From MDN Web Docs):
The Set object lets you store unique values of any type, whether
primitive values or object references
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>