Fill gaps in NVD3.js - javascript

I am trying to make horizontal grouped stacked bar graph in NVD3.js.
Everything works great until I got a "gap" in my JSON data like bellow:
[{
"key": "Education & news",
"values": [{
"label": "2014-02-26",
"value": 702
}, {
"label": "2014-02-27",
"value": 204
}, {
"label": "2014-02-28",
"value": 3213
}]
}, {
"key": "Entertainment",
"values": [{
"label": "2014-02-26",
"value": 21
},
//Missing entry for 2014-02-27
{
"label": "2014-02-28",
"value": 3213
}]
}]
The error which I got is Uncaught TypeError: Cannot read property '1' of undefined in d3.js. The example and the error of the problem I put on http://jsfiddle.net/vaa3V/
Can I somehow fill gaps automatically?

#shabeer90's comment is on track. You can use underscore.js to get the domain values and apply a sensible default.
//Find domain values
DEFAULT = 0
defaults = _.chain(data)
.pluck('values')
.flatten().pluck('label')
.uniq()
.value()
.map( function(item){ return {label:item, value:DEFAULT} })
// Group by 'label' so we can apply defaults
defaults = _.groupBy(defaults, 'label'))
// Apply defaults
_.each(data, function (series) {
grouped = _.groupBy(series.values, 'label')
series.values = _.flatten( _.defaults(grouped, defaults))
})
Should give you:
[
{
"key": "Education & news",
"values": [
{
"label": "2014-02-26",
"value": 702
},
{
"label": "2014-02-27",
"value": 204
},
{
"label": "2014-02-28",
"value": 3213
}
]
},
{
"key": "Entertainment",
"values": [
{
"label": "2014-02-26",
"value": 21
},
{
"label": "2014-02-28",
"value": 3213
},
{
"label": "2014-02-27",
"value": 0
}
]
}
]

Related

A better way to deal with objects using many nested forEach?

I would like to know if there is a better way to deal with nested forEach when it comes to dealing with objects with properties that are nested arrays themselves.
My object (summarized):
{
...,
"tx_responses": [
{
...
"logs" : [
{
...,
"events": [
{
"type": "coin_received",
"attributes": [
{
"key": "receiver",
"value": "somesome"
},
{
"key": "amount",
"value": "somesome"
}
]
},
...
{
"type": "transfer",
"attributes": [
{
"key": "recipient",
"value": "somesome"
},
{
"key": "sender",
"value": "somesome"
},
{
"key": "amount",
"value": "somesome"
}
]
},
{
"type": "withdraw_rewards",
"attributes": [
{
"key": "amount",
"value": "somesomesomehere"
},
{
"key": "validator",
"value": "somesome"
}
]
},
...
]
}
],
...
I am essentially trying to extract all { key: 'amount', value: 'somesomesomehere' } objects in the "attributes" array of the "type" : "withdraw_rewards" object in the "events" array.
Currently this is the code I wrote to carry out my task:
getWithdrawnAmounts: async(del_addr_) => {
let withdrawnAmounts = [];
const res = await axios.get("some_url_that_uses_del_addr_");
res.data.tx_responses.forEach(txr => {
txr.logs.forEach(log => {
log.events.forEach(evnt => {
if (evnt.type == "withdraw_rewards") {
evnt.attributes.forEach(attr => {
if (attr.key == "amount") {
withdrawnAmounts.push(attr);
}
})
}
})
})
});
return withdrawnAmounts;
}
The code helps me to get what I need, but I was wondering if there is a better way to write my code so that I dont have to use so many nested .forEach methods. I was wondering if I should use .flat() or .flatMap() but I'm curious to know how would people approach this?
Thank you!
You are going to have to call some kind of iteration for each level you're going deeper.
Now, there is a more functional way to get the desired data, using flatMap and filter:
const data = { "tx_responses": [{ "logs": [{ "events": [{ "type": "coin_received", "attributes": [{ "key": "receiver", "value": "somesome" }, { "key": "amount", "value": "somesome" }]}, { "type": "transfer", "attributes": [{ "key": "recipient", "value": "somesome" }, { "key": "sender", "value": "somesome" }, { "key": "amount", "value": "somesome" }]}, { "type": "withdraw_rewards", "attributes": [{ "key": "amount", "value": "somesomesomehere" }, { "key": "validator", "value": "somesome" }]}]}]}]};
const result = data.tx_responses
.flatMap(r => r.logs
.flatMap(l => l.events.filter(e => e.type === 'withdraw_rewards')
.flatMap(e => e.attributes.filter(a => a.key === 'amount'))
)
);
console.log(result);

How to get values from nested array of objects - React JS

I am trying to the values from my data.json which consists of array of objects. I am trying to get the values by using map method on json data. My Json dat structure is like Array->Object->Array-Object([{[{}]}]). This is how the data is structured in Json. I have written down the Json data and logic to get the values down. Whenever I am trying to get the values from (inner array of object) I am ending up with undefined. Any one could assist me how to resolve this issue. Thanks in advance!
[
{
"key": "row-0",
"cells": [
{
"key": "cell-0",
"id": "ID-0",
"headerName": "Name",
"CustomerName": "ABC"
},
{
"key": "cell-1",
"id": "ID-1",
"headerName": "RegID",
"CustomerID": "P-01"
},
{
"key": "cell-2",
"id": "ID-2",
"headerName": "Detail",
"Deatil": "Abc"
}
]
},
{
"key": "row-1",
"cells": [
{
"key": "cell-1",
"id": "ID-1",
"headerName": "Name",
"CustomerName": "CDE"
},
{
"key": "cell-2",
"id": "ID-2",
"headerName": "RegID",
"CustomerID": "P-02"
},
{
"key": "cell-3",
"id": "ID-3",
"headerName": "Detail",
"Deatil": "CDE"
}
]
}
]
//Logic
{mockData.map((values, index) => {
console.log("VALUES", values);
return values.cells.map(({ headerName, ...rest }) => {
console.log("JSON", JSON.stringify(rest));
console.log("REST", rest.CustomerName);---> getting undefined(I tried many approach everything is giving me undefined)
});
})}
I have created an example for you
As mentioned in the above comment.. elements without customerName will give undefined.
let mockData = [{
"key": "row-0",
"cells": [{
"key": "cell-0",
"id": "ID-0",
"headerName": "Name",
"CustomerName": "ABC"
},
{
"key": "cell-1",
"id": "ID-1",
"headerName": "RegID",
"CustomerID": "P-01"
},
{
"key": "cell-2",
"id": "ID-2",
"headerName": "Detail",
"Deatil": "Abc"
}
]
},
{
"key": "row-1",
"cells": [{
"key": "cell-1",
"id": "ID-1",
"headerName": "Name",
"CustomerName": "CDE"
},
{
"key": "cell-2",
"id": "ID-2",
"headerName": "RegID",
"CustomerID": "P-02"
},
{
"key": "cell-3",
"id": "ID-3",
"headerName": "Detail",
"Deatil": "CDE"
}
]
}
];
mockData.map((values, index) => {
console.log("VALUES", values);
return values.cells.map(({
headerName,
...rest
}) => {
console.log("JSON",rest);
console.log("Key:", rest.key);
console.log("ID:", rest.id);
console.log("CustomerName:", rest.CustomerName ? rest.CustomerName : 'NA' );
});
})

Look up values in an array using looping forEach Google Apps Script Javascript

I have an object that looks like the following {key: id numbers}
var obj = {
"c4ecb": {id: [3]},
"a4269": {id: [34,36]},
"d76fa": {id: [54,55,60,61]},
"58cb5": {id: [67]}
}
How do I loop each above id in the following array, and return the label?
var response =
{
"success": true,
"data": [
{
"key": "c4ecb",
"name": "fruits",
"options": [
{
"label": "strawberry",
"id": 3
},
{
"label": "apple",
"id": 4
},
{
"label": "pineapple",
"id": 5
},
{
"label": "Other",
"id": 31
}
],
}
]
},
{
"success": true,
"data": [
{
"key": "a4269",
"name": "vegetables",
"options": [
{
"label": "lettuce",
"id": 34
},
{
"label": "cucumber",
"id": 35
},
{
"label": "radish",
"id": 36
}
],
}
]
},
{
"success": true,
"data": [
{
"key": "d76fa",
"name": "pasta",
"options": [
{
"label": "spaghetti",
"id": 54
},
{
"label": "rigatoni",
"id": 55
},
{
"label": "linguine",
"id": 56
},
{
"label": "lasagna",
"id": 60
},
{
"label": "fettuccine",
"id": 61
}
],
}
]
}
Finally, what I want to do is look up the key and return a string of id values.
For example, input c4ecb and output strawberry. Input a4269 and output lettuce, radish. Input d76fa and output "spaghetti, rigatoni, lasagna, fettuccine"
I think to join the multiple labels output into one string I could use something like
array.data.vegetables.map(vegetables => vegetables.value).join(', ')].toString();
So in the end I want to have something like
var fruits = [some code that outputs "strawberry"];
var vegetables = [some code that outputs "lettuce, radish"];
var pasta = [some code that outputs "spaghetti, rigatoni, lasagna, fettuccine"];
What I've tried so far:
The following loop will return the id only if there is one id to be called for: e.g. only in case one where {id: 3} but returns null in cases like {id: 34,36} (because it's looking for '34,36' in id, which doesn't exist - I need to look for each one individually.
response.data.forEach(({key, options}) => {
if (obj[key]) {
options.forEach(({id, label}) => {
if (id == obj[key].id) obj[key].label = label;
});
}
});
console.log(obj)
Filter the response object to focus on the category that matches the id.
Map over the options array and select the items which appear in obj[id].
Finally convert the filtered results to a string.
See filteredLabelsAsString() function below for implementation.
var obj = {
"c4ecb": {"id": [3]},
"a4269": {"id": [34,36]},
"d76fa": {"id": [54,55,60,61]},
"58cb5": {"id": [67]}
}
var response =
[{
"success": true,
"data": [
{
"key": "c4ecb",
"name": "fruits",
"options": [
{
"label": "strawberry",
"id": 3
},
{
"label": "apple",
"id": 4
},
{
"label": "pineapple",
"id": 5
},
{
"label": "Other",
"id": 31
}
],
}
]
},
{
"success": true,
"data": [
{
"key": "a4269",
"name": "vegetables",
"options": [
{
"label": "lettuce",
"id": 34
},
{
"label": "cucumber",
"id": 35
},
{
"label": "radish",
"id": 36
}
],
}
]
},
{
"success": true,
"data": [
{
"key": "d76fa",
"name": "pasta",
"options": [
{
"label": "spaghetti",
"id": 54
},
{
"label": "rigatoni",
"id": 55
},
{
"label": "linguine",
"id": 56
},
{
"label": "lasagna",
"id": 60
},
{
"label": "fettuccine",
"id": 61
}
],
}
]
}];
function filteredLabelsAsString(obj_key, obj, content=response) {
// sanity check: obj must contain obj_key
if (Object.keys(obj).includes(obj_key)) {
return content.filter((item) => {
// filter content using value of obj_key
return item.data[0].key == obj_key;
}).map((item) => {
// item : { success: true, data: [] }
// map over options array
return item.data[0].options.map((opt) => {
// option : {id, label}
// return the label if the id is in the obj object's list
if (obj[item.data[0].key].id.includes(opt.id))
return opt.label;
}).filter((label) => {
// filter out empty items
return label !== undefined;
});
}).join(",");
}
// if obj does not contain obj_key return empty string
return "";
}
console.log("fruits: " + filteredLabelsAsString("c4ecb", obj));
console.log("vegetables: " + filteredLabelsAsString("a4269", obj));
console.log("pasta: " + filteredLabelsAsString("d76fa", obj));

Filter data from Json to new Array which can be used in HighChartsJs

I am currently working with HighCharts JS. For data to be shown in HighCharts, I must have the final data as follows:
[
{
name: 'Performing',
data: [1941404, 1028717, 697370, 0, 0, 0]
},
{
name: 'Non performing',
data: [0, 0, 0, 1759908, 890857, 280235]
},
{
name: 'Substandard',
data: [0, 0, 863825, 0, 0, 0]
},
{
name: 'Written-off',
data: [0, 0, 0, 0, 0, 77146]
}
]
'Data' is an array of 6 objects which populate the xAxis of the chart.
However, I have the following data which is being supplied via MongoDb
const chartData = [
{
"_id": {
"data": "90 - 180",
"status": "Non Performing"
},
"value": 1759908
},
{
"_id": {
"data": "360",
"status": "Written-off"
},
"value": 77146
},
{
"_id": {
"data": "360",
"status": "Non Performing"
},
"value": 280235
},
{
"_id": {
"data": "30 - 90",
"status": "Substandard"
},
"value": 863825
},
{
"_id": {
"data": "30 - 90",
"status": "Performing"
},
"value": 697370
},
{
"_id": {
"data": "180 - 360",
"status": "Non Performing"
},
"value": 890857
},
{
"_id": {
"data": "0 - 30",
"status": "Performing"
},
"value": 1028717
},
{
"_id": {
"data": "0",
"status": "Performing"
},
"value": 1941404
}
]
I need to filter through the latter code so it ends up like the former code. It is very important that in the data array, we end up with 6 objects to make sure we populate the entire xAxis of Highcharts, hence we see lots of zeros, where no data was supplied.
I really hope this clears things up. Thank you to all those who have help. I apologise for being so vague from the offset.
QUICK NOTE The order of the data array is as follows: 0, 0-30, 30-90, 90-180, 180-360, 360
EDITTED
So this is the code I am using at the moment:
const data = this.chartData
let series
series = Object.values(data.reduce((acc, currVal) => {
acc[currVal._id.status] = acc[currVal._id.status] || {
name: currVal._id.status,
data: []
};
acc[currVal._id.status].data.push(currVal.totalBookValue) //push the year to data array after converting the same to a Number
return acc //return the accumulator
}, {}))
This kind of works, but its not populating the Data array with 6 elements.
Your problem can be solved via a bit of traversal using reduce() to reach object form and map() to return to array form (assuming the 0 to 360 list you posted is complete).
See below for a practical example.
// Input.
const input = [
{
"_id": {
"data": "90 - 180",
"status": "Non Performing"
},
"value": 1759908
},
{
"_id": {
"data": "360",
"status": "Written-off"
},
"value": 77146
},
{
"_id": {
"data": "360",
"status": "Non Performing"
},
"value": 280235
},
{
"_id": {
"data": "30 - 90",
"status": "Substandard"
},
"value": 863825
},
{
"_id": {
"data": "30 - 90",
"status": "Performing"
},
"value": 697370
},
{
"_id": {
"data": "180 - 360",
"status": "Non Performing"
},
"value": 890857
},
{
"_id": {
"data": "0 - 30",
"status": "Performing"
},
"value": 1028717
},
{
"_id": {
"data": "0",
"status": "Performing"
},
"value": 1941404
}
]
// Depth.
const depth = ['0', '0 - 30', '30 - 90', '90 - 180', '180 - 360', '360']
// Object Form.
const objectform = input.reduce((accumulator, x) => {
const { _id, value } = x // _id. Value.
let { data, status } = _id // Status.
status = status.toLowerCase() // Lower Case.
const point = {...accumulator[status], [data]: value} // Data.
return {...accumulator, [status]: point} // Update + Return Accumulator.
}, {})
// Output.
const output = Object.keys(objectform).map((key) => {
return {
name: key, // Name.
data: depth.map((frame) => objectform[key][frame] || 0) // Data.
}
})
// Log.
console.log(output)

Filter method on multidimensional array

I have an id and i to filter a multidimensional array with these. My code is:
service.fakedata.map(f=>{
f.results.map(r=>{
r = r.filter(m=> m.rId !== id)
})
})
and my array is :
"services": [
{
"id": "1839f72e-fa73-47de-b119-49fb971a5730",
"name": "In I/O Route",
"url": "http://wwww.in.io/[param1]/[param2]",
"inputParams": [
{
"id": "e74a6229-4c08-43a1-961f-abeb887fa90e",
"name": "in1",
"datatype": "string"
},
{
"id": "e74a6229-4c08-43a1-961f-abeb887fa90o",
"name": "in2",
"datatype": "string"
}
],
"isArrayResult": false,
"results": [
{
"id": "ef7c98db-9f12-45a8-b3fb-7d09a82abe3d",
"name": "out1",
"datatype": "string",
"fakedatatype": [
"address",
"city"
]
},
{
"id": "9b178ded-af27-43df-920f-daab5ad439b9",
"name": "out2",
"datatype": "string",
"fakedatatype": [
"internet",
"url"
]
}
],
"routeParameters": [
"param1",
"param2"
],
"fakedata": [
{
"id": "b0376694-9612-43d2-93ed-c74264df962e",
"url": "http://wwww.in.io/wood/good",
"params": [
{
"key": "param1",
"value": "wood"
},
{
"key": "param2",
"value": "good"
}
],
"inputParams": [
{
"iId":"e74a6229-4c08-43a1-961f-abeb887fa90e",
"key": "in1",
"value": "m"
},
{
"iId":"e74a6229-4c08-43a1-961f-abeb887fa90o",
"key": "in2",
"value": "z"
}
],
"results": [
{
"rId": "ef7c98db-9f12-45a8-b3fb-7d09a82abe3d",
"key": "out1",
"value": "result1",
"fakedatatype": [
"address",
"city"
]
},
{
"rId": "9b178ded-af27-43df-920f-daab5ad439b9",
"key": "out2",
"value": "result2",
"fakedatatype": [
"internet",
"url"
]
}
]
}
]
}
]
In this case filter is working (when I check with console.log) but it doesn't change fakedata array.
What was wrong with my code?
From https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map
The map() method creates a new array with the results of calling a provided function on every element in the calling array.
base on #H77 suggestion i change my code and now my code is look like this and everything work well
const s = service.fakedata.map(f=>{
f.results = f.results.map(r=>{
return r.filter(m=> m.rId !== id)
})
})

Categories