make combinations of two array in json - javascript

Input :
"options": [
{
"name": "Color",
"values": [
"Blue",
"Black"
]
},
{
"name": "Size",
"values": [
"Small",
"Large"
]
}
]
Output: "variants": [
{
"option1": "Blue",
"option2": "Small"
},
{
"option1": "Blue",
"option2": "Large"
},
{
"option1": "Black",
"option2": "Small"
},
{
"option1": "Black",
"option2": "Large"
}
]
How to solve this using recursion ?The options array can contain multiple name and i need the above out to be displayed. Can it be done using cartesian product i guess

You could take an iterative and recursive approach for getting all option combinations.
function getCombinations(array) {
function iter(i, temp) {
if (i === array.length) {
result.push(temp.reduce(function (o, v, i) {
o['option' + (i + 1)] = v;
return o;
}, {}));
return;
}
array[i].values.forEach(function (a) {
iter(i + 1, temp.concat(a));
});
}
var result = [];
iter(0, []);
return result;
}
var options = [{ name: "Color", values: ["Blue", "Black"] }, { name: "Size", values: ["155", "159"] }, { name: 'Material', values: ['Sand', 'Clay', 'Mud'] }],
variants = getCombinations(options);
console.log(variants);
.as-console-wrapper { max-height: 100% !important; top: 0; }
ES6
function getCombinations(array) {
function iter(i, temp) {
if (i === array.length) {
result.push(temp);
return;
}
array[i].values.forEach(a => iter(i + 1, Object.assign({}, temp, { ['option' + (i + 1)]: a })));
}
var result = [];
iter(0, {});
return result;
}
var options = [{ name: "Color", values: ["Blue", "Black"] }, { name: "Size", values: ["155", "159"] }, { name: 'Material', values: ['Sand', 'Clay', 'Mud'] }],
variants = getCombinations(options);
console.log(variants);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can use nested Array.map() calls, create the objects, and flatten the sub arrays using Array.concat():
const options = [{"name":"Color","values":["Blue","Black"]},{"name":"Size","values":["155","159"]}]
const [{ values: colors }, { values: sizes }] = options
const result = [].concat(...colors.map((option1) => sizes.map((option2) => ({
option1,
option2
}))))
console.log(result)

var myarray = {"options": [
{
"name": "Color",
"values": [
"Blue",
"Black"
]
},
{
"name": "Size",
"values": [
"155",
"159"
]
}
]};
const key = myarray.options[0].values;
const value =myarray.options[1].values;
const output = _.zipWith(key, value, (key, value)=> ({ key, value }));
console.log(output);
<script src="https://cdn.jsdelivr.net/lodash/4.16.6/lodash.min.js"></script>

Related

Flatten JSON objects in JavaScript

I have a JSON response and I need to flatten some objects according to conditions.
In my case, the condition is if object === "$"
I also need to flatten some array to string only. What is the best way to manage this? Any lib recommendations?
The final goal would be to use it as a GraphQL schema.
The JSON response is:
[
{
"$":{
"product_id":"110015888426488829026466000222",
"name":"Belstaff Trialmaster Pro W Motorradjacke Schwarz",
"sku_number":"B000134394",
"manufacturer_name":"BELSTAFF",
"part_number":"42050011L81N03379005436"
},
"discount":[
{
"$":{
"currency":"EUR"
},
"type":[
"amount"
]
}
],
"price":[
{
"$":{
"currency":"EUR"
},
"sale":[
"1195.00"
],
"retail":[
"1195.00"
]
}
],
"shipping":[
{
"cost":[
{
"$":{
"currency":"EUR"
},
"amount":[
"0.00"
],
"currency":[
"EUR"
]
}
],
"information":[
"3–7 Werktage"
],
"availability":[
"in-stock"
]
}
],
"attributeClass":[
{
"$":{
"class_id":"60"
},
"Product_Type":[
"Motorcycle"
],
"Size":[
"36,38,40,42,44,46,48"
],
"Material":[
"Waxed Leather"
],
"Color":[
"Schwarz"
],
"Gender":[
"Female"
],
"Age":[
"Adult"
]
}
]
},
...
]
And I want
[
{
"product_id":"110015888426488829026466000222",
"name":"Belstaff Trialmaster Pro W Motorradjacke Schwarz",
"sku_number":"B000134394",
"manufacturer_name":"BELSTAFF",
"part_number":"42050011L81N03379005436"
"discount":{
"currency":"EUR"
"type":"amount"
},
"price":{
"currency": "EUR",
"sale": "1195.00",
"retail": "1195.00"
},
"shipping":[
"cost":{
"currency": "EUR"
"amount": "0.00",
"currency": "EUR"
},
"information":"3–7 Werktage",
"availability": "in-stock"
],
"attributeClass":{
"class_id":"60",
"Product_Type": "Motorcycle",
"Size": "36,38,40,42,44,46,48",
"Material": "Waxed Leather",
"Color": "Schwarz",
"Gender": "Female",
"Age": "Adult"
}
},
...
]
What I tried:
const flatJson = async data => {
try {
const newData = await Promise.all(json.map( async (item, i) => {
xxx
}));
return newData
}
catch(err) {
console.log(err)
}
};
You could convert the various types to single objects.
const convert = object => {
if (!object || typeof object !== 'object') return object;
if (Array.isArray(object)) {
return object.every(v => typeof v === 'string')
? object.join()
: Object.assign({}, ...object.map(convert));
}
return Object.fromEntries(Object.entries(object).flatMap(([k, v]) =>
k === '$'
? Object.entries(v).map(([k, v]) => [k, convert(v)])
: [[k, convert(v)]]
));
};
var data = [{ $: { product_id: "110015888426488829026466000222", name: "Belstaff Trialmaster Pro W Motorradjacke Schwarz", sku_number: "B000134394", manufacturer_name: "BELSTAFF", part_number: "42050011L81N03379005436" }, discount: [{ $: { currency: "EUR" }, type: ["amount"] }], price: [{ $: { currency: "EUR" }, sale: ["1195.00"], retail: ["1195.00"] }], shipping: [{ cost: [{ $: { currency: "EUR" }, amount: ["0.00"], currency: ["EUR"] }], information: ["3–7 Werktage"], availability: ["in-stock"] }], attributeClass: [{ $: { class_id: "60" }, Product_Type: ["Motorcycle"], Size: ["36,38,40,42,44,46,48"], Material: ["Waxed Leather"], Color: ["Schwarz"], Gender: ["Female"], Age: ["Adult"] }] }],
result = data.map(convert);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can try this recursion code
function recurseData(data, level) {
var newdata = {};
for(var prop in data) {
if( data[prop].constructor == Object && prop=="$") {
var data1 = recurseData(data[prop], level+1);
for(var prop1 in data1)
newdata[prop1] = data1[prop1];
}
else if(data[prop].constructor == Object){
newdata[prop] = recurseData(data[prop],level+1);
}
else if(data[prop].constructor == Array && level > 0 && data[prop][0].constructor != Object && data[prop].length==1){
newdata[prop] = data[prop][0];
}
else if(data[prop].constructor == Array) {
newdata[prop] = [];
for(var i=0;i<data[prop].length;i++)
newdata[prop].push( recurseData(data[prop][i],level+1) );
}
else if(prop!="$")
newdata[prop] = data[prop];
}
return newdata;
}
data[0] = recurseData(data[0], 0);
is simple loops through all tree structure and detects for a key $

Returning mutated object with some key modified using spread operator

In the below object, I want to increment the value of data[1]'s val by 1, and leave everything unchanged, how can I achieve it?
const state =
{
"data": [{
"val": 1,
"other": 10
},
{
"val": 11,
"other": 100
},
{
"val": 100,
"other": 1000
}
]
}
I want the mutated object to be like this-
{
"data": [{
"val": 1,
"other": 10
},
{
"val": 10,
"other": 100
},
{
"val": 100,
"other": 1000
}
]
}
I know that I can change the value directly like this- state.data[1].val = state.data[1].val+1, but I want to achieve the same using spread operator, is it possible to achieve it using spread operator?
Somthing like this-
const mutatedState = {
...state,
data: [...state.data]
}
Get the data out of the object. And use like this
const state = { "data": [{ "val": 1, "other": 10 }, { "val": 11, "other": 100 }, { "val": 100, "other": 1000 } ] }
const {data} = state;
let res = {
...state,
data:[
data[0],
{...data[1],val:data[1].val+ 1},
...data.slice(2)
]
}
console.log(result)
You could assign parts of the array/objects.
var object = { data: [{ val: 1, other: 10 }, { val: 10, other: 100 }, { val: 100, other: 1000 }] },
result = {
...object,
data: Object.assign(
[...object.data],
{
1: Object.assign(
{},
object.data[1],
{ val: object.data[1].val + 1 }
)
}
)
};
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
With a bit help of some functional helpers that is actually quite elegant:
const mapOne = (index, mapper) => array => array.map((it, i) => i === index ? mapper(it) : it);
const lens = (key, mapper) => obj => ({ ...obj, [key]: mapper(obj[key]) });
// somewhere
this.setState(mapOne(1, lens("val", it => it + 1)));

How to extract object from another object?

I have this array object, and I'm trying to create another object out of it. I already have a solution but I think there may be a shorter way of doing what I'm doing. Does anyone know how to make this code shorter or another way using lodash or pure javascript? Thanks a lot in advance!
{
firstName: Mike,
lastName : Brown
}
so far my code works and looks like this:
let response = [
{
"Name": "hobby",
"Value": "poker"
},
{
"Name": "privacy_id",
"Value": "1112"
}, {
"Name": "given_name",
"Value": "Mike"
},
{
"Name": "family_name",
"Value": "Brown"
},
{
"Name": "email",
"Value": "test#email.com"
}
]
const newObj = {};
_.forEach(response, function(obj) {
if(obj.Name === 'given_name') { newObj.firstName = obj.Value}
if(obj.Name === 'family_name'){ newObj.lastName = obj.Value}
});
console.log(newObj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
This is a good usecase for Array.prototype.reduce since what you want is to transform an array into something - something being an object in your case:
const newObj = response.reduce((acc, curr) => {
acc[curr.Name] = curr.Value;
return acc;
}, {});
This would transform:
const response = [
{
'Name': 'given_name',
'Value': 'Mike'
},
{
'Name': 'family_name',
'Value': 'Brown'
}
]
into:
{
'given_name': 'Mike',
'family_name': 'Brown'
}
Now, if you want to change the naming of the key, you could use some sort of mapping:
const NameMapping = {
given_name: 'firstName',
family_name: 'lastName'
};
const response = [
{
'Name': 'given_name',
'Value': 'Mike'
},
{
'Name': 'family_name',
'Value': 'Brown'
}
]
const newObj = response.reduce((acc, curr) => {
if (NameMapping[curr.Name] === undefined)
return acc;
acc[NameMapping[curr.Name]] = curr.Value;
return acc;
}, {});
So your newObj would look like this:
{
firstName: 'Mike',
familyName: 'Brown'
}
If you are sure that response contains both the object with the key given_name and the object with the key family_name, you could write this way:
const newObj = {
'given_name': response.filter(el => el.Name ==='given_name')[0].Value,
'family_name': response.filter(el => el.Name ==='family_name')[0].Value,
}
There's the fiddle:
let response = [
{
"Name": "hobby",
"Value": "poker"
},
{
"Name": "privacy_id",
"Value": "1112"
}, {
"Name": "given_name",
"Value": "Mike"
},
{
"Name": "family_name",
"Value": "Brown"
},
{
"Name": "email",
"Value": "test#email.com"
}
]
const newObj = {
'given_name': response.filter(el => el.Name ==='given_name')[0].Value,
'family_name': response.filter(el => el.Name ==='family_name')[0].Value,
}
console.log(newObj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

How to replace all the values in JSON structure using javascript?

If I have JSON structure like this, I want to parse this JSON replace all values with '{param.keyName}', if the key value is array of objects than need to generate its value like {param.headKey[index]keyName}
{
"resources": [
{
"name": "prod",
"type": "local",
"properties": {
"zone": "asia",
"disks": [
{
"sizeGb": 3,
"diskType": "boot",
"name": "backup"
},
{
"sizeGb": 4,
"diskType": "ssd",
"name": "cache"
}
]
}
}
]
}
The output of the function should be JSON like this where all the values should be replaced with the mapping. If there is any array of objects than it should prefixed by array index like {param.disks0_name}, where disks is an array of objects.
{
"resources": [
{
"name": "prod",
"type": "local",
"properties": {
"zone": "{param.zone}",
"disks": [
{
"sizeGb": '{param.disks0_sizeGb}',
"diskType": '{param.disks0_diskType}',
"name": "{param.disks0_name}"
},
{
"sizeGb": '{param.disks1_zone}',
"diskType": '{param.disks1_diskType}',
"name": "{param.disks1_name}"
}
]
}
}
]
}
You could look for the arrays/objects and iterate them for getting the path for the last property.
Format as desired (which is unclear for nested arrays).
function formatPath(path) {
return `{${path.join('.')}}`;
}
function getPath(object, path = []) {
return Object.assign(
Array.isArray(object) ? [] : {},
...Object.entries(object).map(([k, v]) => ({
[k]: v && typeof v === 'object'
? getPath(v, path.concat(k))
: formatPath(path.concat(k))
}))
);
}
var data = { resources: [{ name: "prod", type: "local", properties: { zone: "asia", disks: [{ sizeGb: 3, diskType: "boot", name: "backup" }, { sizeGb: 4, diskType: "ssd", name: "cache" }] } }] };
data = { resources: data.resources.map(o => Object.assign({}, o, { properties: getPath(o.properties, ['param']) })) };
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
ES5
function formatPath(path) {
return '{' + path.join('.') + '}';
}
function getPath(object, path) {
path = path || [];
return Object.keys(object).reduce(
function (r, k) {
r[k] = object[k] && typeof object[k] === 'object'
? getPath(object[k], path.concat(k))
: formatPath(path.concat(k));
return r;
},
Array.isArray(object) ? [] : {}
);
}
var data = { resources: [{ name: "prod", type: "local", properties: { zone: "asia", disks: [{ sizeGb: 3, diskType: "boot", name: "backup" }, { sizeGb: 4, diskType: "ssd", name: "cache" }] } }] };
data = { resources: data.resources.map(function (o) {
return Object.keys(o).reduce(function (r, k) {
r[k] = k === 'properties'
? getPath(o.properties, ['param'])
: o[k];
return r;
}, {});
}) };
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Print array of objects entire path

Good Day People
I have an array of objects and I need to print out the path of each node value and last print key and value for special (by name) node.
This is the array of objects or JSON
[{
"Name": "2007",
"Elements": [{
"Name": "country1",
"Elements": [{
"House": "house1",
"water": 1.8
}],
"Data": {}
},
{
"Name": "country2",
"Elements": [{
"Name": "city2",
"Elements": [{
"Name": "neighbourhood2",
"Elements": [{
"House": "house2",
"water": 2.8
}]
}],
"Data": {}
}],
"Data": {}
},
{
"Name": "country3",
"Elements": [{
"House": "house2",
"uni bill": 3.8
}],
"Data": {}
}
],
"Data": {}
}]
The output should be like this
2007 > country1 > house > water: 1.8
2007 > city2 > neighbourhood2 > house2 > electricity: 2.8
2007 > country3 > house > uni bill: 3.8
++++++++++++++ edited +++++++++++++++
function objectToPaths(data) {
var validId = /^[a-z_$][a-z0-9_$]*$/i;
var result = [];
doIt(data, "");
return result;
function doIt(data, s) {
if (data && typeof data === "object") {
if (Array.isArray(data)) {
for (var i = 0; i < data.length; i++) {
doIt(data[i], s + "");
}
} else {
for (var p in data) {
if (validId.test(p)) {
doIt(data[p], s + " > " + data[p]);
} else {
doIt(data[p], s + "");
}
}
}
} else {
result.push(s);
}
}
}
this is a rewrite of a function I found here but I did not get the expected result
+++++++++++++++++++++++ end of the edit +++++++++++++++++++++++
Please help
Thanks in advance
What you are looking for is a Depth First Traversal function that recursively print properties:
function print(arr, path) { // print takes an array an an accumulated path from which it will start printing
arr.forEach(function(obj) { // for each object obj in the array
if(obj.Elements) { // if the object obj has sub elements in it
print(obj.Elements, path + " > " + obj.Name); // then call print on those elements, providin the absolute path to this object
} else { // otherwise (it is a leaf)
const bills = Object.keys(obj)
.filter(key => key !== "House")
.map(key => `${key}: ${obj[key]}`)
.join(', ')
console.log(path.slice(3) + " > " + obj.House + " > " + bills); // print the accumulated path along with the House property of this object (removing the first 3 letters from path which are equal to " > ")
}
});
};
var arr = [{"Name":"2007","Elements":[{"Name":"country1","Elements":[{"House":"house1","water":1.8}],"Data":{}},{"Name":"country2","Elements":[{"Name":"city2","Elements":[{"Name":"neighbourhood2","Elements":[{"House":"house2","water":2.8}]}],"Data":{}}],"Data":{}},{"Name":"country3","Elements":[{"House":"house2","uni bill":3.8}],"Data":{}}],"Data":{}}];
print(arr, "");
You could take a function for iterating and collect the path to the last object.
function iter(array, path) {
path = path || [];
array.forEach(function (o) {
if (o.Elements) {
return iter(o.Elements, path.concat(o.Name));
}
Object.keys(o).forEach(function (k) {
if (k !== 'House') {
console.log(path.concat(o.House, k).join(' > ') + ': ' + o[k]);
}
});
});
}
var data = [{ Name: "2007", Elements: [{ Name: "country1", Elements: [{ House: "house1", water: 1.8 }], Data: {} }, { Name: "country2", Elements: [{ Name: "city2", Elements: [{ Name: "neighbourhood2", Elements: [{ House: "house2", water: 2.8 }] }], Data: {} }], Data: {} }, { Name: "country3", Elements: [{ House: "house2", "uni bill": 3.8 }], Data: {} }], Data: {} }];
iter(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources