How to iterate JSON with multiple levels? - javascript

Welcome, I got a problem with JSON file. I would like to iterate through it and get every status into array, but I got stuck. I load this file and parse it. Then I tried to use forEach but it did not worked. Thanks for help!
[{
"offers": [{
"advertiser_api_id": 12,
"status": 1
}, {
"advertiser_api_id": 13,
"status": 0
}]
}]

I assume this will be in javascript. You can try the following:
for (x in json[0].offers) {
console.log(json[0].offers[x].status);
}

You have got an array Of Objects inside Array.
Firstly, you need to parse the data from JSON to object using JSON.parse(data); Then, access the object offers. using the parsedData[0].offers object and then iterate over the array to get the status.
var data = `[{
"offers": [{
"advertiser_api_id": 12,
"status": 1
}, {
"advertiser_api_id": 13,
"status": 0
}]
}]`;
var parsedData = JSON.parse(data);
var result = [];
parsedData[0].offers.forEach((currentValue) => result.push(currentValue["status"]));
console.log(result)

You can use map function:
var data = [{
"offers": [{
"advertiser_api_id": 12,
"status": 1
}, {
"advertiser_api_id": 13,
"status": 0
}]
}]
var stats = data[0].offers.map(function(item) {
return item.status;
})
console.log(stats);

This loops through the object and prints the data to console - you likely did not reach the correct array to loop through.
var data = [{
"offers": [{
"advertiser_api_id": 12,
"status": 1
}, {
"advertiser_api_id": 13,
"status": 0
}]
}];
data[0].offers.forEach(function(element) {
console.log(element.status);
});

This will work
statuses = []
JSON.parse(data)[0].offers.forEach(x => statuses.push(x.status))

A recursive approach
var item = [{
"offers": [{
"advertiser_api_id": 12,
"status": 1
}, {
"advertiser_api_id": 13,
"status": 0
}]
}];
var statusArray = [];
function getStatusForALlLevels(item){
if(item.offers instanceof Array){
for(var i=0;i<item.offers.length;i++){
getStatusForALlLevels(item.offers[i]);
}
}else{
statusArray.push(item.status);
}
}
getStatusForALlLevels(item[0]);
console.log(statusArray);

You could use an iterative and recursive approach for getting status from multiple nested objects.
var data = [{ offers: [{ advertiser_api_id: 12, status: 1 }, { advertiser_api_ii: 13, status: 0 }] }],
status = data.reduce(function iter(r, o) {
if ('status' in o) {
r.push(o.status);
}
Object.keys(o).forEach(function (k) {
if (Array.isArray(o[k])) {
r = o[k].reduce(iter, r);
}
});
return r;
}, []);
console.log(status);

Related

find min from arrays of objects

I have list of statuses where not started is the minimum , followed by submitted and last is complete.
So if we apply the logic on the data since there are 2 status on the current array of object below which are "Not Started" and "Submitted" so the function should return Not Started because not started is the min.
If there is only 1 item in an array then get the only status as a result.
How do we filter the array of objects and get the min based on the order in statuses array. Thanks for any idea.
#currentCode
getMin(id:number , key:string) {
let data = [
{
"id": 14,
"status": "Submitted",
},
{
"id": 15,
"status": "Not Started",
}
]
let min = Math.min(...data.map(item => item.status));
console.log('result' , min)
}
#order of statuses
statuses: any[] = [
{ viewValue: 'Not Started', value: 1 },
{ viewValue: 'Submitted', value: 2 },
{ viewValue: 'Complete', value: 3 },
]
#sample array of objects - result Not Started
data = [
{
"id": 14,
"status": "Submitted",
},
{
"id": 15,
"status": "Not Started",
}
]
#sample array of objects - result Submitted
data = [
{
"id": 14,
"status": "Submitted",
},
{
"id": 17,
"status": "Complete",
}
]
#sample array of objects - result Complete , since there is only 1 get the only status
data = [
{
"id": 17,
"status": "Complete",
}
]
You need to look up the status from data in your statuses array.
const data = [
{
"id": 14,
"status": "Submitted",
},
{
"id": 17,
"status": "Complete",
}
];
const statuses = [
{ viewValue: 'Not Started', value: 1 },
{ viewValue: 'Submitted', value: 2 },
{ viewValue: 'Complete', value: 3 },
]
let min = Math.min(...data.map(item => statuses.find(st => st.viewValue === item.status).value));
let minStatus = statuses.find(st => st.value === min).viewValue;
console.log(minStatus);
A straightforward solution is to simply sort the array by statuses value, which then gives you access to the complete ordering, and the entire object for each position. Here first mapping the statuses array to an object keyed by viewValue to directly access status values.
const data = [
{ "id": 14, "status": "Submitted", },
{ "id": 17, "status": "Complete", },
{ "id": 15, "status": "Not Started", },
{ "id": 15, "status": null, }
];
const statuses = [
{ viewValue: 'Not Started', value: 1 },
{ viewValue: 'Submitted', value: 2 },
{ viewValue: 'Complete', value: 3 },
];
const statusMap = Object.fromEntries(statuses
.map(({ viewValue, value }) => [viewValue, value])
);
const sorted = data
.filter(d => Object.hasOwn(statusMap, d.status))
.sort((a, b) => statusMap[a.status] - statusMap[b.status]);
const min = sorted.at(0);
const max = sorted.at(-1);
console.log('min:', min.status);
console.log('max:', max.status);
// or the complete objects
console.log({ min, max })
Edit
Added a filter() call before sorting to remove datum that don't have a status that appears in statusMap using Object.hasOwn. This also makes the original spread redundant since filter() returns a new array thus avoiding mutation of the original data array with the sort() call.

Create JSON dynamically with dynamic keys and values in Express Js

I am fetching API into my Express server which has several JSON key value pairs in one array.
For Example:
[{
"quality": "best",
"url": "https://someurlhere.example/?someparameters"
},
{
"quality": "medium",
"url": "https://someurlhere1.example/?someparameters"
}]
And I want to create an array of JSON of that received data in this Format:
[{
"best": "https://someurlhere.example/?someparameters"
},
{
"medium": "https://someurlhere1.example/?someparameters"
}]
I have tried doing this by using for loop
for(let i=0; i < formats.length; i++){
arr.push({
`${formats[i].quality}` : `${formats[i].url}`
})
}
But it didn't work for me.
Please help me in achieving this.
Thanks in Advance :)
You could use the map function and create a new object from it.
For example:
let prevArr = [{
"quality": "best",
"url": "https://someurlhere.example/?someparameters"
}, {
"quality": "medium",
"url": "https://someurlhere1.example/?someparameters"
}]; // Replace with your array
let newArr = [];
let obj = {};
prevArr.map(function(x) {
obj = {};
obj[x["quality"]] = x.url;
newArr.push(obj);
});
const input = [{
"quality": "best",
"url": "https://someurlhere.example/?someparameters"
}, {
"quality": "medium",
"url": "https://someurlhere1.example/?someparameters"
}];
const result = input.map((v, i) => {
return {
[v["quality"]]: v["url"]
}
});
console.log(result)

Transforming the array of object for Highcharts input?

I am really struggling with the Object transformation. I have an array of Object and want to transform it into Highcharts multi line chart input. I want to get the unique dates first sorted from low to high, which will go into x axis and then transform the Original based on ID and date. The length for each ID.data is going to remain same (for whatever date count is not available for that date it will come as null)
Original:
[
{
"date": "1997-09-29",
"Count": 100,
"ID": "AB12-R"
},
{
"date": "1997-12-30",
"Count": 104.7,
"ID": "AB13-R"
},
{
"date": "1998-03-30",
"Count": 981,
"ID": "BA12-R"
},
{
"date": "1998-06-01",
"Count": 341,
"ID": "BA12-R"
}
]
Transformed:
[{
Identiy : 'AB12-R',
data : [100,null,null,null]
},
{
Identiy : 'AB13-R',
data : [null,104.7,null,null]
},{
Identiy : 'BA12-R',
data : [null,null,981,341]
}]
I have tried with reduce but nothing is working it seems. I am able to group it by ID but not able to handle the null and missing count, Can someone please help me here ?
This is what i have tried:
const result = Original.reduce(function (r, a) {
r[a.ID] = r[a.ID] || [];
r[a.ID].push(a);
return r;
}, Object.create(null));
console.log({'Transformed':result})
Take a look at this solution:
const hcData = [];
data.forEach((d, i) => {
const checkIfExist = hcData.find(data => data.id === d["ID"])
if (checkIfExist) {
checkIfExist.data[i] = d["Count"];
} else {
const initialData = [...Array(data.length)]
initialData.fill(null, 0, data.length)
initialData[i] = d["Count"];
hcData.push({
data: initialData,
id: d["ID"]
})
}
})
Demo: https://jsfiddle.net/BlackLabel/k2dg1wns/

How to Convert an Array of Objects into an Object of Associative Arrays in Javascript

I am receiving the following structure from a system. I am attempting to bend it into the form needed for a particular graph utilizing chartjs. Given the JSON data structure … an array of objects in an object:
{
"chart": [
{
"date": "2018-10-29",
"done": 3,
"todo": 10
},
{
"date": "2018-10-30",
"done": 4,
"todo": 7
},
{
"date": "2018-10-31",
"done": 5,
"todo": 12
}
]
}
I need the desired JSON data structure ... an object of arrays (in one array, in one object)
{
"chart": [{
"date": [
"2018-10-29",
"2018-10-29",
"2018-10-31"
],
"done": [
3,
4,
5
],
"todo": [
10,
7,
12
]
}]
}
I have attempted to use the .map function but I don't seem to have the correct map-fu.
You could take an object and get all keys with ther values in single array.
var data = { chart: [{ date: "2018-10-29", done: 3, todo: 10 }, { date: "2018-10-30", done: 4, todo: 7 }, { date: "2018-10-31", done: 5, todo: 12 }] },
result = { chart: data.chart.reduce((r, o) => {
Object.entries(o).forEach(([k, v]) => (r[k] = r[k] || []).push(v));
return r;
}, {})
};
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
What about using reduce ?
const output = input.reduce((acc, curr) => ({
date: acc.date.concat(curr.date),
done: acc.done.concat(curr.done),
todo: acc.todo.concat(curr.todo),
}), { date: [], done: [], todo: [] });
const chartData = {
chart: [output],
};
Reference for reduce is here : https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/reduce
Here's a very explicit solution. There may be some slicker Javascript solutions; certainly you can do multiple .map calls, but that makes it less efficient.
// Variables
var dates = [];
var doneValues = [];
var todoValues = [];
// Loop through the original data once, collect the data.
originalJSON.forEach(function(data) {
dates.push(data["date"]);
doneValues .push(data["done"]);
todoValues .push(data["todo"]);
});
// Put it all together.
return {"chart": [{"date": dates, "done": doneValues , "todo": todoValues}]};
Modify it to suit your needs.

combine json array into one json array by id

I want to merge item and purchases array of json into one by matching their property value.
Here's the source :
{
"item": [
{
"invoiceId": 1
},
{
"invoiceId": 2
},
{
"invoiceId": 3
}
],
"purchase": [
{
"id": "1",
"date": "12/1/2014"
},
{
"id": "2",
"date": "12/1/2014"
},
{
"id": "3",
"date": "12/1/2014"
}
]
}
I want to produce something like this :
{
"combined": [
{
"invoiceId": 1,
"id": "1",
"date": "12/1/2014"
},
{
"invoiceId": 2,
"id": "2",
"date": "12/1/2014"
},
{
"invoiceId": 3,
"id": "3",
"date": "12/1/2014"
}
]
}
How can I match the item.invoiceId with purchase.id?
Solution
assuming obj is your object
var new_obj = {combined:[]};
obj["purchase"].forEach(function(a) {
obj["item"].forEach(function(b){
if (+b["invoiceId"]===(+a["id"])) {
a["invoiceId"] = b["invoiceId"] || 0;//WILL MAKE INVOICEID 0 IF IT IS NOT DEFINE. CHANGE 0 TO YOUR NEEDS
new_obj.combined.push(a);
}
});
});
How it works
The first .forEach() loops through obj.purchase. Then we loop through obj.item To check if their is a matching invoiceId (if you don't need to make sure their is a matching invoiceId, use the alternate code). Then, we simply add a new value to the new_obj
The result (copied from console) is:
{
"combined":[
{
"id":"1",
"date":"12/1/2014",
"invoiceId":1
},
{
"id":"2",
"date":"12/1/2014",
"invoiceId":2
},
{
"id":"3",
"date":"12/1/2014",
"invoiceId":3
}
]
}
Alternative Code
Use this if you don't need to make sure, invoiceId is there
var new_obj = {combined:[]};
obj["purchase"].forEach(function(a){a["invoiceId"]=a["id"];new_obj.combined.push(a);});
One way of achieving what you want will be
var result = {};
var getMatchingPurchase = function(invoiceId) {
return data.purchase.filter(function(purchase) {
return invoiceId == purchase.id;
})[0];
};
result.combined = data.item.map(function(invoice) {
var purchase = getMatchingPurchase(invoice.invoiceId);
return {
invoiceId: invoice.invoiceId,
id: purchase.id,
date: purchase.date
};
});
console.log(result);
It will print like bellow
{ combined:
[ { invoiceId: 1, id: '1', date: '12/1/2014' },
{ invoiceId: 2, id: '2', date: '12/1/2014' },
{ invoiceId: 3, id: '3', date: '12/1/2014' } ] }
Note:- I'm using map and filter functions which are not supported in IE8. If you want to use in IE8 you have to use for loop.
If you have to support old browsers like IE8 (poor guy...), note that the native forEach might not be supported, in this case you can use lodash for cross-browser compatibility:
function getCombinedResult(source){
var combinedList = [];
_.each(source.item, function(item){
_.each(source.purchase, function(purchase){
if (item['invoiceId'].toString() != purchase['id'].toString()) return;
var combinedItem = _.extend(item, purchase)
combinedList.push(combinedItem);
});
})
return {"combined": combinedList};
}

Categories