Merge json entries with d3 - javascript

I am making some visualisations and need to merge certain json entries.
The problem is, that the json file that gets called is a bit messed up. Certain entries need to be merged and others should be kept the same.
The json i get is something like this:
[
{
"label": "de",
"visits": 80,
},
{
"label": "fr",
"visits": 80,
},
{
"label": "/de",
"visits": 80,
},
{
"label": "/fr",
"visits": 80,
},
{
"label": "Aktuell",
"visits": 80,
},
{
"label": "fr/Aktuell",
"visits": 80,
},
{
"label": "index",
"visits": 80,
}
]
What i need is:
[
{
"label": "de",
"visits": 160,
},
{
"label": "fr",
"visits": 160,
},
{
"label": "Aktuell",
"visits": 160,
},
{
"label": "index",
"visits": 80,
}
]
The entries with labels "de" and "/de" as well as "fr" and "/fr" or "fr/Aktuell" and "Aktuell" need to be merged while other entries schould be kept the same like "index".
Is there a way to do this using d3?
I guess only using javascript I would have to get the json file, then search for the entries, create a new entry and add up the numbers to then take the original json, delete those entries and add the new entries... although i also wouldn't know exactly how to do that either.
And it also isn't the best solution, since i have to generate a second object that will have to be processed and slows the whole system down. We are having performance issues already and i don't want to create new bottle necks.
Another problem is, that i need to be able to extend the list of entries that should be merged...
Is there a fast and simple way of doing this?
I know this seems like a "hey, i am too lazy, do it for me" post.
I can assure you it isn't. It's more like a " i googled, i read, i tried, i sweared and i cried. now i am asking the professionals for help" post.
UPDATE:
I don't want to filter only on the last part of '/'. It was just an example. The filter would me more like: combine all [en, EN, /en, english, anglais] to one with label en. Also combine all [de, DE, /de, deutsch, german] to one with label de. Keep all others that are not combined.

Javascript should be enough for this:
function clean(arr) {
var obj = {};
// obj will contain a map with keys being 'label' and value being 'visits'
for (var i = 0; i < arr.length; i++) {
var labelParts = arr[i].label.split('/')
var label = labelParts[labelParts.length-1]
// Do a bit of filtering on the label
if(typeof obj[label]!=='undefined') {
obj[label] += arr[i].visits
} else {
obj[label] = arr[i].visits
}
}
return Object.keys(obj).map(function (key) { return {label:key,visits:obj[key]}; });
// Re-create an array out of the object
}
I assumed you wanted to filter on the last part after '/'.
jsFiddle: http://jsfiddle.net/4peN9/2/

I'd look into D3's nest operations. It shouldn't be too hard to specify the appropriate key and rollup functions to get what you want. Something to the effect of:
var cleanup = function(l) {
var i = l.indexOf("/");
return i < 0 ? l : l.substring(i+1, l.length);
};
var getVisits = function(d) { return d.visits; };
var sum = function(a, b) { return a + b; };
var nest = d3.nest()
.key(function(d) { return cleanup(d.label); })
.rollup(function(a) { return a.map(getVisits).reduce(sum); });
jsFiddle: http://jsfiddle.net/4peN9/3/

I now wrote a quite ugly but functional javascript script for this. I am sure that it could be done more efficiently but this shall do for me atm.
And it goes something like this:
function clean(arr)
{
mrgLst = [
{
"valid": "de",
"invalid": ["/de"]
},
{
"valid": "fr",
"invalid": ["/fr"]
},
{
"valid": "en",
"invalid": ["/en"]
},
{
"valid": "it",
"invalid": ["/it"]
},
{
"valid": "/index",
"invalid": ["/index.html"]
}
]
var obj = [];
for (var i = 0; i < mrgLst.length; i++)
{
var valid = mrgLst[i].valid;
var invalid = mrgLst[i].invalid;
for (var j = 0; j < arr.length; j++)
{
var test0 = arr[j].label
if (test0 == valid)
{
var subObj = {};
subObj["label"] = valid
subObj["visits"] = arr[j].visits
for (var k = 0; k < arr.length; k++)
{
var test1 = arr[k].label
for (x in invalid)
{
if (test1 == invalid[x])
{
subObj["label"] = valid
subObj["visits"] += arr[k].visits
}
}
}
}
}
if (subObj != undefined)
{
obj.push(subObj)
}
}
for (var i = 0; i < arr.length; i++)
{
var original = arr[i]
var org = {}
var dont = false
for (var j = 0; j < mrgLst.length; j++)
{
var test0 = mrgLst[j].valid
var test1 = mrgLst[j].invalid
if (original.label == test0)
{
dont = true
}
for (x in test1)
{
if (original.label == test1[x])
{
dont = true
}
}
}
if (dont == false)
{
org["label"] = original.label
org["visits"] = arr[i].visits
obj.push(org)
}
}
return obj
}
Just as reference for anybody that has a similar problem. Doesn't have much to do with d3 anymore but the output is used with d3...

Related

How to make string to json with javascript? (like A//a1,A//a2,A//a3//a31 ..)

How to convert a string to JSON with javascript or jQuery? I've been thinking all day, but I do not get a good idea.
This task is to dynamically create the treeview in the client side (ASP.Net). My idea is to convert the string to an object and convert to JSON type. (String -> object -> JSON) I tried, but the day is gone. It is difficult to construct 2 more depth like A->a3->a31.
String is
var sString = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
and JSON format is
{
"title": "A",
"key": "1",
"folder": true,
"children": [{
"title": "a1",
"key": "2"
}, {
"title": "a2",
"key": "3"
}, {
"title": "a3",
"key": "4",
"folder": true,
"children": [{
"title": "a31",
"key": "5"
}...
}]
}
(This is fancytreeview plugin)
‘//‘ is depth and ‘,’ is split.
Please help me..
Edit)
I want to turn ‘sString’ to JSON format.. but It’s ok just JSON type string.
Please understand that my sentence is strange because my native language is not English.
Edit2)
oh.. I want to convert the string to an object and then convert it back to JSON format. I do not have the confidence to convert that string into JSON format right away. Because there are more than 8000 variants. If It’s can, let me know how.
I believe this can be done without recursion:
var string = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
// Take all the roots
var roots = string.split(',');
// We will attach it to every node and keep it incrementing
var key = 1;
// The final result will be in this object
var result = [];
// Loop through to found roots
roots.forEach(function(root) {
// Take all the children
var items = root.split('//');
var parent = result;
// Loop through the available children
items.forEach(function(item, i) {
// Find if the current item exists in the tree
var child = getChild(parent, item);
if (!child) {
child = {
title: item,
key: key++
}
// This will ensure that the current node is a folder only
// if there are more children
if (i < items.length - 1) {
child.folder = true;
child.children = [];
}
// Attach this node to parent
parent.push(child);
}
parent = child.children;
});
});
console.log(result);
// Utility function to find a node in a collection of nodes by title
function getChild(parent, title) {
for (var i = 0; i < parent.length; i++) {
if (parent[i].title === title) {
return parent[i];
}
}
}
This is the draft code which came in my mind at first. I believe it can be improved further in terms of complexity.
var key = 1; // keys start at 1
let addPaths = (root, paths) => {
if (!paths || paths.length == 0)
return;
let path = paths.shift();
//add nodes for the current path
addNodes(root, path.split('//'));
// keep going until all paths have been processed
addPaths(root, paths);
};
let addNodes = (root, nodeList) => {
if (!nodeList || nodeList.length == 0)
return;
let title = nodeList.shift();
// find node under root with matching title
let isRootNode = Array.isArray(root);
node = (isRootNode ? root : root.children || []).find((node) => {
return node.title == title;
});
if (!node){
node = {
title: title,
key: key++
}
// are we at root of object?
if (isRootNode)
root.push(node);
else
{
if (!root.children)
root.children = [];
root.children.push(node);
root.folder = true;
}
}
addNodes(node, nodeList);
};
let parse = (string) => {
let object = [];
let nodes = string.split(',');
addPaths(object, nodes);
return object
};
console.log(JSON.stringify(parse("A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2"), null, 2));
Which results in:
[
{
"title": "A",
"key": 1,
"children": [
{
"title": "a1",
"key": 2
},
{
"title": "a2",
"key": 3
},
{
"title": "a3",
"key": 4,
"children": [
{
"title": "a31",
"key": 5
},
{
"title": "a32",
"key": 6
}
],
"folder": true
}
],
"folder": true
},
{
"title": "B",
"key": 7
},
{
"title": "C",
"key": 8,
"children": [
{
"title": "c1",
"key": 9
},
{
"title": "c2",
"key": 10
}
],
"folder": true
}
]
Try below code. I have used associative array to store already processed folder for faster lookup.
I hope it helps you.
var sString = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";
var sArr = sString.split(","); // We will split it by comma so that we can iterate through its items.
var output = []; // Final result will be stored here.
var hash = {}; // It used to keep track of itemObjectect's position for faster lookup.
var counter = 1; // Its value will be used to assign to key;
for(var i = 0; i < sArr.length; i++){
var items = sArr[i].split("//");
var itemObject = {}; // Object to store value of each item.
var parentItemObject = {}; // It will refer to current parentObject during iteration.
for(var j = 0; j < items.length; j++){
// Check if item is already processed and stored in hash map.
if(hash.hasOwnProperty(items[j])){
// Check if parent Object value is empty then we will fetch it from hash directly.
if(isEmpty(parentItemObject)){
parentItemObject = output[hash[items[j]]];
}
else{
// It is parent element but is child of another element. Then we will fetch it from it's children array.
if(typeof parentItemObject.children !== "undefined"){
parentItemObject = parentItemObject.children[hash[items[j]]];
}
}
continue;
}
itemObject.title = items[j];
itemObject.key = counter++;
// Check if it is a folder item.
if(j != items.length -1){
itemObject.folder = true;
itemObject.children = [];
if(isEmpty(parentItemObject)){
parentItemObject = itemObject;
hash[itemObject.title] = output.length;
output.push(itemObject);
}
else{
if(typeof parentItemObject.children !== "undefined"){
hash[itemObject.title] = parentItemObject.children.length;
parentItemObject.children.push(itemObject);
}
parentItemObject = itemObject;
}
}
else{
if(isEmpty(parentItemObject)){
parentItemObject = itemObject;
hash[itemObject.title] = output.length;
output.push(itemObject);
}
if(typeof parentItemObject.children !== "undefined"){
hash[itemObject.title] = parentItemObject.children.length;
parentItemObject.children.push(itemObject);
}
}
itemObject = {};
}
//console.log(items);
}
function isEmpty(itemObject) {
return Object.keys(itemObject).length === 0;
}
//console.log(hash);
console.log(JSON.stringify(output,null,2));

Javascript, loop through array and apply other functions

I have an array of objects,
var out = [{
"type": "1",
"from": "13052033555",
"to": "4444444",
"amount": "40000",
"date": 1461575799,
"status": "1"
}, {
"type": "2",
"from": "13052033555",
"to": "1111111",
"amount": "30000",
"date": 1461575884,
"status": "1"
}...
];
I get only it's values without keys
Now i used this function to get the values from array like this,
I pass array then it returns only values without keys
function foo(a) {
var values = [];
for (var i = 0; i < a.length; i++) {
var obj = a[i];
var arr = Object.keys(obj).map(function(k) {
return obj[k]
});
values.push("[" + arr + "],");
}
return values.join('');
}
Then it returns the values data without keys like this,
["1","13052033555","4444444","40000",1461575799,"1"],
["2","13052033555","1111111","30000",1461575884,"1"],
My question is how can I loop through the values and only apply a method to 5th value of each data array ?
All you have to do in foo is call your fn on arr[4] before continuing with the loop.
arr[4] = fn(arr[4])
This is of course, assuming you don't need to do this after the fact for whatever reason. If that is the case, you can use another for loop like your original code, and just modify the 5th element in each array as specified above, except it would look more like
for (var i = 0; i < outerArray.length; i++) {
outerArray[i][4] = fn(outerArray[i][4])
}
Now that we covered how to do it with your current code, I would also suggest that you don't do this for any real world application. If you want to modify data on a specific object property for a list of objects, you should do it with the object property name (key) and not later on the array of values using an index. This prevents any confusion that could arise from the fact that objects do not have a guaranteed order. Here's an example of how I would do this, assuming you want to modify date:
function foo(a) {
return a.map(function(obj) {
return Object.keys(obj).map(function(k) {
return k === 'date' ? fn(obj[k]) : obj[k]
})
})
}
This way you target the property you want but also don't modify the original object.
Note You should replace fn with your desired function :)
Edit Per your request, here is how you might extend it to check for other property names
function foo(a) {
return a.map(function(obj) {
var values = []
Object.keys(obj).forEach(function(k) {
if (k === 'date') {
values.push(fn(obj[k]))
} else if (k !== 'type') {
values.push(obj[k])
}
})
return values
})
}
var out = [{
"type": "1",
"from": "13052033555",
"to": "4444444",
"amount": "40000",
"date": 1461575799,
"status": "1"
}, {
"type": "2",
"from": "13052033555",
"to": "1111111",
"amount": "30000",
"date": 1461575884,
"status": "1"
}
];
function foo(a) {
var values = [];
for (var i = 0; i < a.length; i++) {
var obj = a[i];
var arr = Object.keys(obj).map(function(k) {
return obj[k]
});
values.push("[" + arr + "],");
}
return values.join('');
}
function myFn(elem){
console.log(elem);
return elem;
}
var arr = foo(out);
arr = JSON.parse("["+arr.substring(0,arr.length-1)+"]")
arr.forEach(function(elem){
return myFn(elem[4]);
});
console.log(arr);

JSON to Multidimensional Javascript Object

I'm loading an external JSON file into javascript, the JSON file looks like this:
[
{
"name":"Apple",
"year":8,
"records_lost":12367232
},
{
"name":"178.com",
"year":7,
"records_lost":10000000
},
{
"name":"Accendo Insurance Co. ",
"year":7,
"records_lost":175350
}
]
Eventually, I want to access the data via a Javascript object like this (don't mind the syntax). The point is that name will be a parent with its own meta-data.
"Apple":
"year":8,
"records_lost":12367232
"178.com":
"year":7,
"records_lost":10000000
This is the code I've already written for this part, which doesn't make name parent yet and only saves the last row of the JSON file into the array (+= instead of = would fix this, but delivers ugly values, obviously).
function initJSON() {
loadJSON(function(response) {
var JSONParse = JSON.parse(response);
var i;
for (i in JSONParse) {
JSONdata.name.i = JSONParse[i].name;
JSONdata.year = JSONParse[i].year;
JSONdata.recl = JSONParse[i].records_lost;
}
});
}
initJSON();
Thanks in forward.
Try utilizing Array.prototype.map() , delete operator
var data = [{
"name": "Apple",
"year": 8,
"records_lost": 12367232
}, {
"name": "178.com",
"year": 7,
"records_lost": 10000000
}, {
"name": "Accendo Insurance Co. ",
"year": 7,
"records_lost": 175350
}];
var res = data.map(function(val, key) {
var obj = {};
obj[val.name] = val;
delete obj[val.name].name;
return obj
});
document.getElementsByTagName("pre")[0].textContent = JSON.stringify(res, null, 4);
<pre></pre>
it should be :
var i;
for (i in JSONParse) {
JSONdata.name = i.name;
JSONdata.year = i.year;
JSONdata.recl = i.records_lost;
}
unless your loop was different:
var i;
for (i = 0; i < JSONParse.length; i++) {
JSONdata.name = JSONParse[i].name;
JSONdata.year = JSONParse[i].year;
JSONdata.recl = JSONParse[i].records_lost;
}

iterate over json array - how to get the name / key of collection

I get a json object from an API ; I'm getting (inside another collection - but that doesn't matter) a collection of keys like so:
{ "Id": "64a66e2c-38fa-41dd-9183-8b1bd18a3c87",
"fields": [
{ "Name": "x1" },
{ "Name": "x2" },
{ "Name": "x3" }
],
"weeks": [ "2015-25", "2015-26", "2015-27" ],
"series": {
"M1": [ 6376, 17877, 22592 ],
"M2": [ 700, 702, 702 ],
"M3": [ null, 1, 1 ],
"M4": [ null, 5889, 10275 ]
}
}
the series itself (like M1) have values (1 - 1 etc) I can read and use,
but I actually need the name of the serie (like "M1").
So I iterate over the collection series and over each values within that serie without any difficulty, but how do I get the name of the serie itself - that name is (not in this example) quite descriptive and usefull
$.each(data.series, function (rowIndex, row) {
var attempt = data.series[rowIndex]; // nope this is [1,1,1,2] or something
for (var i = 0; i < row.length; i++) {
var value = row[i]; // yep this is one of the values
};
});
how do I get "serie1" into var attempt?
You already get the key as the first param when passing in a javascript object to the $.each() iterator.
$.each(data.series, function (rowIndex, row) {
window.alert(rowIndex);
});
I think what everyone is trying to say is that a more descriptive signature of the jquery.each callback is:
$.each(data.series, function (key, val) {
var attempt = data.series[key]; // nope this is [1,1,1,2] or something
for (var i = 0; i < val.length; i++) {
var value = val[i]; // yep this is one of the values
};
});
so to get what you want, simple change to:
$.each(data.series, function (key, val) {
var attempt = key;
for (var i = 0; i < val.length; i++) {
var value = val[i];
};
});

Select specific JSON key's value to sum it

For a JSON object that looks like this:
[{
"gemeente": "amsterdam",
"stadsdeel": "centrum",
"adres": "damrak 28-30",
"bvo": "2000",
},
{
"gemeente": "amsterdam",
"stadsdeel": "centrum",
"adres": "damrak 66",
"bvo": "1500",
"": ""
}]
I'm trying to get a total sum of the bvo value. the output would in this case be 3500. Then I need to use this number for a data visualisation. I'm working whith d3.js but any Javascript solution would do.
D3 handles this quite well with d3.sum, without proper testing I think the syntax should be something like
d3.sum(list, function(d) { return d.bvo; });
What you need is
var list = [{
"gemeente": "amsterdam",
"stadsdeel": "centrum",
"adres": "damrak 28-30",
"bvo": "2000",
},
{
"gemeente": "amsterdam",
"stadsdeel": "centrum",
"adres": "damrak 66",
"bvo": "1500",
"": ""
}];
// code to sum the values
var sum = 0;
for(var len = list.length, i=0; i < len; i++){ // for each object in the list
var item = list[i];
sum += parseInt(item.bvo, 10); // parse its "bvo" element as integer and add it to the sum
}
// output to console the sum.. or do whatever you want with the sum here..
console.log(sum);
I got the answer with regular Javascript:
var SumByTipe = {};
for (i in data) {
var currtipe = data[i].tipe;
if (currtipe in SumByTipe) {
for (j in data[i]) {
if (j != "tipe" && j != "mc") {
SumByTipe[currtipe][j + '_total'] += parseFloat(data[i][j]);
}
}
}
else {
var firstSum = {};
for (j in data[i]) {
if (j != "tipe" && j != "mc"){
firstSum[j + '_total'] = parseFloat(data[i][j]);
}
}
SumByTipe[currtipe] = firstSum;
}
}
console.log(SumByTipe);
Though I don't really get how it works, it worked fine. :-)

Categories