Flatten JSON objects in JavaScript - 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 $

Related

Javascript removing nested array elements using filter()

I have the below object, and want to remove the element("virAddrSeq": "345").
var state={
"todos": [
{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [
{
"virAddrSeq": "345"
},
{
"virAddrSeq": "34775"
}
]
}
]
}
I have tried the below way but getting all the records with out removing the element.
const newObj = Object.assign({}, state, {
todos: state.todos.filter(todoObj => (todoObj.vpainfo.filter(({virAddrSeq}) => (virAddrSeq != "345"))))
})
console.log(newObj)
var state = {
"todos": [{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [{
"virAddrSeq": "345"
},
{
"virAddrSeq": "34775"
}
]
}]
}
console.log(
state.todos.map(todo => ({...todo, vpainfo: todo.vpainfo.filter(({virAddrSeq}) => virAddrSeq!= 345)}))
)
var state = {
"todos": [{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [{
"virAddrSeq": "345"
},
{
"virAddrSeq": "34775"
}
]
}]
}
for (const s of state.todos) {
let findKey =s.vpainfo.find(x => x.virAddrSeq == '345')
let index = s.vpainfo.indexOf(findKey)
if(findKey && index > -1) s.vpainfo.splice(index,1)
}
console.log(state)

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; }

How to get one json object format from another JSON format

Maybe this question has already been asked and answered somewhere but after searching for more than 3 hrs I'm asking this question.
Below is my JSON data
var my_data = [
{
"TempRture_qc": 4,
"VoltAGE": 44.09722,
"TempRture": 22.32,
"VoltAGE_qc": 55,
"_time": "2018-08-07T03:39:29.001Z"
},
{
"TempRture_qc": 2,
"VoltAGE": 42.09722,
"TempRture": 22.12,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:30.006Z"
},
{
"TempRture_qc": 1,
"VoltAGE": 43.09722,
"TempRture": 22.82,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:31.009Z"
}
];
desired output i need
[
{
"name": "TempRture_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":4},
{"name":"2018-08-07T03:39:30.006Z","y":2},
{"name":"2018-08-07T03:39:33.017Z","y":1}
]
},
{
"name": "VoltAGE",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":44.09722},
{"name":"2018-08-07T03:39:30.006Z","y":42.09722},
{"name":"2018-08-07T03:39:33.017Z","y":43.09722}
]
},
{
"name": "TempRture",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":22.32},
{"name":"2018-08-07T03:39:30.006Z","y":22.12},
{"name":"2018-08-07T03:39:33.017Z","y":22.82}
]
},
{
"name": "VoltAGE_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":55},
{"name":"2018-08-07T03:39:30.006Z","y":0},
{"name":"2018-08-07T03:39:33.017Z","y":0}
]
}
]
for getting this above output i have tried below code.
var accounting = [];
var fieldName = {};
for (var x in obj){
var mykey = Object.keys(obj[x]);
for (var mk in mykey){
if(mykey[mk]=='VoltAGE'){
fieldName.name = mykey[mk];
// accounting.push({
// "name":mykey[mk]
// })
}
if(mykey[mk]=='TempRture'){
fieldName.name = mykey[mk];
}
// console.log(mykey[mk]); //to get the key name
}
accounting.push({
"name" : obj[x]._time,
"y" : obj[x][employees.name],
})
fieldName.data = accounting;
}
console.log(fieldName );
by doing this what I'm getting is below JSON
{ name: 'TempRture',
data:
[ { name: '2018-08-07T03:39:29.001Z', y: 22.32 },
{ name: '2018-08-07T03:39:32.014Z', y: 22.12 },
{ name: '2018-08-07T03:39:33.017Z', y: 22.82 } ] }
I'm not able to understand how I will get the data in one JSON object.
For a solution with low time complexity, try .reduceing into an object indexed by keys of the inner object, creating a { name, data: [] } at that key in the accumulator if it doesn't exist there yet. Then, push to the data array, and get the values of the whole object:
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
console.log(Object.values(
my_data.reduce((a, { _time, ...obj }) => {
Object.entries(obj).forEach(([name, val]) => {
if (!a[name]) a[name] = { name, data: [] };
a[name].data.push({ name: _time, y: val });
});
return a;
}, {})
));
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
var keys = Object.keys(my_data[0])
var result= [];
for(i = 0; i<keys.length-1; i++) {
var obj = {name: keys[i],data: []}
obj.data = my_data.map(val=>({name: val["_time"], y: val[keys[i]]}));
result.push(obj);
}
console.log(result)
An understandable answer with map, findIndex and forEach functions will be
var my_data = [{ "TempRture_qc": 4, "VoltAGE": 44.09722, "TempRture": 22.32, "VoltAGE_qc": 55, "_time": "2018-08-07T03:39:29.001Z" }, { "TempRture_qc": 2, "VoltAGE": 42.09722, "TempRture": 22.12, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:30.006Z" }, { "TempRture_qc": 1, "VoltAGE": 43.09722, "TempRture": 22.82, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:31.009Z" } ],
result = [];
my_data.map(itm => {
let keys = Object.keys(itm);
keys.forEach(iitt => {
if (iitt != '_time') {
let index = result.findIndex(ii => {
return ii.name == iitt;
})
if (index == -1) {
result.push({
name: iitt,
data: []
});
result[result.length - 1].data.push({
name: itm["_time"],
y: itm[iitt]
})
} else {
result[index].data.push({
name: itm["_time"],
y: itm[iitt]
});
}
}
})
})
console.log(result)

make combinations of two array in json

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>

Categories

Resources