Reputation: 7732
I'm working with denormalized JSON structures but I need nested JSON to iterate over them in multiple levels on the client side. I'm stumped how to do this elegantly.
Can you think how to easily transform JSON of the form
[{ category: xyz, attribute: 1},{ category: xyz, attribute: 2}]
into a hierarchical format:
[{category: xyz, attributes: [1,2]}] ?
Much much thanks for any solutions.
Upvotes: 1
Views: 1204
Reputation: 14729
When I have to do stuff like this I usually build external arrays with metadata to improve code readability. So don't do it like this if your JSON is huge.
function normalize(json) {
var categories= [];
var norm= [];
for (var i=0; i < json.length; i++) {
if (categories.indexOf(json[i].category) !== -1) {
categories.push(json[i].category);
}
}
for (var i=0; i < categories.length; i++) {
norm.push({ category: categories[i]; attributes: []});
}
for (var i=0; i < json.length; i++) {
norm[categories.indexOf(json[i].category)].attributes.push(json[i].attribute);
}
return norm;
}
My answer is O(n*log(m) + n + n*m), where n is the length of the outermost json and m is the number of categories, but the n*log(m) and n*m are the indexOf part and it is done in native code by the browser. I'm too lazy to do proper big O comparison with the other answers, but this is clearly slower.
Upvotes: 1
Reputation: 3110
I do this all the time, though usually on the server side. The code looks something like this:
function normalize(key, outputKey, data)
{
var results = [];
var record;
for(var i=0; i<data.length; i++)
{
if(!record || record[key] != data[i][key])
{
record = {};
record[outputKey] = [];
record[key] = data[i][key];
results.push(record);
}
delete data[i][key];
record[outputKey].push(data[i]);
}
return results;
}
Here's the output:
[
{
"category": "xyz",
"values": [
{ "attribute": 1 },
{ "attribute": 2 }
]
}
]
Note that your example output is messed up. You might want to decide if you're trying to group objects or values of an array. The latter is even easier.
EDIT: Of course, you were looking for the boring answer. Here you go:
function collapse(key, attr, data)
{
var results = [];
var record;
for(var i=0; i<data.length; i++)
{
if(!record || record[key] != data[i][key])
{
record = data[i];
record[attr] = [data[i][attr]];
results.push(record);
}
else
{
record[attr].push(data[i][attr]);
}
}
return results;
}
If you call it like this:
var data = [{ category: 'xyz', attribute: 1},{ category: 'xyz', attribute: 2}];
collapse('category', 'attribute', data);
You'll get this output:
[{"category":"xyz","attribute":[1,2]}]
Upvotes: 1
Reputation: 33409
function reorder(arr) {
var heir = {};
arr.forEach(function(i){
for (var j in i) {
if (heir[j]) {
if (heir[j].indexOf(i[j]) == -1) {
heir[j].push(i[j]);
}
} else {
heir[j] = [i[j]];
}
}
});
for (var i in heir) {
if (heir[i].length == 1) {
heir[i] = heir[i][0];
}
}
return heir;
}
This won't perfectly conform to your example, but pretty close:
{ category: 'xyz', attribute: [ 1, 2 ] }
Upvotes: 1