I would like to create a key, value pair for a JSON using d3.nest().
The original JSON appears like this:
[
{
"Country": "Mexico",
"Climate": 100,
"Safety Index": 50,
"Life Expectancy": 80,
"Corruption": 30,
"Per Capita Income": 20
},
{
"Country": "UK",
"Climate": 70,
"Safety Index": 80,
"Life Expectancy": 70,
"Corruption": 70,
"Per Capita Income": 80
},
{
"Country": "US",
"Climate": 80,
"Safety Index": 70,
"Life Expectancy": 90,
"Corruption": 70,
"Per Capita Income": 80
}
]
I would like to transform it into this:
[
{"key": "Mexico", "value":
[
{ "key": "Climate", "value": 100 },
{ "key": "Safety Index", "value": 50 },
{ "key": "Life Expectancy", "value": 80 },
{ "key": "Corruption", "value": 30 },
{ "key": "Per Capita Income", "value": 20 }
]},
{"key": "UK", "value":
[
{ "key": "Climate", "value": 70 },
{ "key": "Safety Index", "value": 80 },
{ "key": "Life Expectancy", "value": 70 },
{ "key": "Corruption", "value": 70 },
{ "key": "Per Capita Income", "value": 80 }
]},
{"key": "US", "value":
[
{ "key": "Climate", "value": 80 },
{ "key": "Safety Index", "value": 70 },
{ "key": "Life Expectancy", "value": 90 },
{ "key": "Corruption", "value": 70 },
{ "key": "Per Capita Income", "value": 80 }
]}
]
My attempt:
var nest = d3.nest()
.key(function(d) { return d.Country; })
.entries(data);
My question is very basic as I'm new to D3. How should I go about changing the structure of the JSON in D3?
EDIT
This is to create a separate bar chart per country based on the different categories similar to this example ( http://bl.ocks.org/mbostock/5872848 ). If there's a better way I should be thinking about this, let me know.
If you want the exact output that you've provided, and insist on using nests for this, you can use:
var nest = d3.nest()
.key(function(d) { return d.Country; })
.rollup(function(d) {
var countryGroupedObject = d[0];
return d3.nest()
.key(function(innerKey) { return innerKey; })
.rollup(function(innerKey) { return countryGroupedObject[innerKey]; })
.entries(Object.keys(countryGroupedObject));
})
.entries(data);
A potential issue here is that you're assuming that each country will be unique (e.g. there won't be two "Mexico" entries). As such the inner nest only operates on d[0] (which will be the first item listed for a given country).
Edit: Actually, this won't be exactly what you asked for, the country key:value will exist in the inner grouping. If that's a problem, it can be ignored by placing the following before the inner nest:
delete countryGroupedObject.Country;
Edit 2: The above code uses nested nest functions simply because the question asked it that way. In real systems, the inner nest makes more sense to me to be pure Javascript, along the lines of:
function getGraphParams(countryObj){
return Object.keys(countryObj)
.filter(function(key){ return key !== "Country"; })
.map(function(key){ return { key: key, value: countryObj[key] }; })
}
Here's a solution without D3:
var a = ...; // Original Array of JSON
var b = [];
a.forEach(function(item) {
var r = {};
r.key = item.Country;
r.value = [];
for (var i in item) {
if (i === "Country") { continue; }
r.value.push({"key": i, "value": item[i]});
}
b.push(r);
});
Not sure why you're only using "key" as your key and "value" for you value keys though, that seems to go against the point using implicit keys and values in JSON. But I assume you have reason to be doing it this way.
You said:
If there's a better way I should be thinking about this, let me know.
If you're going to change your datastructure, why not change it into something like this?
[
["Mexico", 100, 50, 80, 30, 20],
["UK", 70, 80, 70, 70, 80],
["US", 80, 70, 90, 70, 80],
]
It's a lot smaller and contains no redundancies.
If you want your structure to be as close as possible to the data structure of the code example, I would nevertheless recommend sticking with your original data structure, as the CSV file in the original data structure is converted to a data structure pretty much identical to the data structure of your original JSON file.
Related
I have a json object,which contains following data.
var myData = [{
"sNo": "1.0",
"boqName": "Installation of pole 11 KV",
"unit": "Nos",
"opportunityBoqOverHeadMasterList": [{
"opportunityBoqOverHeadMasterId": 14,
"value": 41.3
},
{
"opportunityBoqOverHeadMasterId": 16,
"value": 41.3
},
{
"opportunityBoqOverHeadMasterId": 24,
"value": 100
}
]
},
{
"sNo": "2.0",
"boqName": "Installation of pole 33 KV",
"unit": "Nos",
"opportunityBoqOverHeadMasterList": [{
"opportunityBoqOverHeadMasterId": 15,
"value": 52.92
},
{
"opportunityBoqOverHeadMasterId": 17,
"value": 52.92
},
{
"opportunityBoqOverHeadMasterId": 25,
"value": 0
}
]
},
];
}
i need to display the value of opportunityBoqOverHeadMasterList next to the unit cell.
Please help me to display data in handsontable. Please refer jsfiddle for working example https://jsfiddle.net/Umanath16/aty5wfg7/22/
You could try processing the data. Notice below that I changed your columns array, create a new myNewData variable and create nested loops to format the data.
var columns = [
{
"data": "sNo"
},
{
"data": "boqName"
},
{
"data": "unit"
},
,
{
"data": "Percentage1"
},
{
"data": "Percentage2"
},
{
"data": "Percentage3"
}
];
Change the data obj...
var myNewData = [];
for (var i = 0; i < myData.length; i++) {
myNewData.push({
sNo: myData[i].sNo,
boqName: myData[i].boqName,
unit: myData[i].unit
});
for (var j = 0; j < myData[i].opportunityBoqOverHeadMasterList.length; j++) {
myNewData[i]["Percentage"+(j+1)] = myData[i].opportunityBoqOverHeadMasterList[j].value;
}
}
hot = new Handsontable(example, {
data: myNewData,
rowHeaders: true,
columns: columns,
rowHeaderWidth: 100,
colWidths: 120,
colHeaders: colHeaders
});
}
This is just one possible solution. Please think about possible improvements. What if the data does not have the same fields? It would need to be dynamic regardless of the field names. Also how would you improve the loops, etc.
I already searched for my issue, however, i did not find something, that matches my needs. I want to merge (sum) multiple series of data into a single array. Any dataset that matches the key, shall be summed in the resulting array.
Please see sample data and expected result:
var power1 = [
{
"time": 10,
"power": 100
},
{
"time": 20,
"type": 200
},
{
"time": 30,
"type": 300
}
]
var power2 = [
{
"time": 20,
"type": 200
},
{
"time": 30,
"type": 300
},
{
"time": 40,
"type": 400
}
]
var result = [
{
"time": 10,
"type": 100
},
{
"time": 20,
"type": 400
},
{
"time": 30,
"type": 600
},
{
"time": 40,
"type": 400
}
]
Since this should happen with thousands of items, it should be reasonable fast. Could a Map as intermediate help here?
Thanks in advance
You can concat the two arrays and then perform a reduce operation over it with an object to store the values for each key.
var power1=[{time:10,type:100},{time:20,type:200},{time:30,type:300}],power2=[{time:20,type:200},{time:30,type:300},{time:40,type:400}];
const res = Object.values(
power1.concat(power2).reduce((acc, {type, time})=>{
(acc[time] ??= {time, type: 0}).type += type;
return acc;
}, {})
);
console.log(res);
This may be extremely simple but I've not been able to figure out how to iterate over and access the properties in the following mix (I think) of arrays and nested objects:
myFilters = {
"color_Filter": [{
"name": "BLUE",
"count": 1,
"dataId": "BLUE"
},
{
"name": "Black",
"count": 5,
"dataId": "Black"
},
{
"name": "Blue",
"count": 14,
"dataId": "Blue"
}
],
"size_Filter": [{
"name": "10",
"count": 16,
"dataId": "10"
},
{
"name": "12",
"count": 16,
"dataId": "12"
}
]
}
What would the correct looping structure be here to pull out name, count etc from the above? The desired output is to output a string from the above with color_Filter=BLUE,Black,Blue/size_Filter=10,12
I've tried a few different approaches and none of them have been successful so far.
You could map the entries of the object and create a string for each key. Get the name from the value array using map. Then join the array of strings with a /
const myFilters = {color_Filter:[{name:"BLUE",count:1,dataId:"BLUE"},{name:"Black",count:5,dataId:"Black"},{name:"Blue",count:14,dataId:"Blue"}],size_Filter:[{name:"10",count:16,dataId:"10"},{name:"12",count:16,dataId:"12"}]};
const output = Object.entries(myFilters)
.map(([k,arr]) => `${k}=${arr.map(a => a.name)}`)
.join("/")
console.log(output)
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
}
]
}
]
I am attempting to get just the smiling array under tags then attributes. I have tried both to search and simply select. Every attempt results in an undefined variable. If you could explain how to select the smiling array that would be excellent!
{
"status": "success",
"photos": [{
"url": "http://tinyurl.com/673cksr",
"pid": "F#019cbdb135cff0880096136c4a0b9bad_3547b9aba738e",
"width": 375,
"height": 406,
"tags": [{
"uids": [],
"label": null,
"confirmed": false,
"manual": false,
"width": 30.67,
"height": 28.33,
"yaw": -16,
"roll": -1,
"pitch": 0,
"attributes": {
"face": {
"value": "true",
"confidence": 84
},
"smiling": {
"value": "false",
"confidence": 46
}
},
"points": null,
"similarities": null,
"tid": "TEMP_F#019cbdb135cff0880096136c00d500a7_3547b9aba738e_56.80_41.13_0_1",
"recognizable": true,
"center": {
"x": 56.8,
"y": 41.13
},
"eye_left": {
"x": 66.67,
"y": 35.71,
"confidence": 51,
"id": 449
},
"eye_right": {
"x": 50.67,
"y": 35.47,
"confidence": 57,
"id": 450
},
"mouth_center": {
"x": 60.8,
"y": 51.23,
"confidence": 53,
"id": 615
},
"nose": {
"x": 62.4,
"y": 42.61,
"confidence": 54,
"id": 403
}
}]
}],
"usage": {
"used": 21,
"remaining": 79,
"limit": 100,
"reset_time": 1390111833,
"reset_time_text": "Sun, 19 January 2014 06:10:33 +0000"
},
"operation_id": "edc2f994cd8c4f45b3bc5632fdb27824"
}
This particular code, will aggregate all the smiling attribute from the given object and return that as an Array.
map function will get smiling attribute from each and every tag
concat function will flatten all the attributes and returns a single array per photo.
reduce function will gather all the attributes all the photos and accumulate the result in result and that will be returned at the end.
var result = data.photos.reduce(function(result, currentPhoto) {
return result.concat(currentPhoto.tags.map(function(currentTag) {
return currentTag.attributes.smiling;
}));
}, []);
console.log(result);
Output
[ { value: 'false', confidence: 46 } ]
JSON.parse(json).photos[0].tags[0].attributes.smiling
obj.photos[0].tags[0].attributes.smiling
The best way would be to loop through the tags, instead of hardcoding 0 in there
obj.photos.forEach(function(photo){
photo.tags.forEach(function(tag){
tag.attributes.smiling; // here it is
});
});
It's a bit tricky since your JSON object is a mixture of objects and arrays, but here's how you would get to the "smiling" object (it's an object since there's no associative arrays in JavaScript):
var smiling_object = obj["photos"][0]["tags"][0]["attributes"]["smiling"];
Then you if you want to do something with it:
var some_var = smiling_object["value"];
var some_other_var = smiling_object["confidence"];
alert("The combined string is " + some_var + " and " + some_other_var);