Convert flat array of objects into nested array of objects
Original JSON data (flat table):
[
{"id":"1","first_name":"Jason","last_name":"Martin","start_date":"1996-07-25","end_date":"2006-07-25","salary":"1234.56","city":"Toronto","description":"Programmer","department":"Finance","active":"1"},
{"id":"2","first_name":"Alison","last_name":"Mathews","start_date":"1976-03-21","end_date":"1986-02-21","salary":"6661.78","city":"Vancouver","description":"Tester","department":"Finance","active":"1"},
{"id":"3","first_name":"James","last_name":"Smith","start_date":"1978-12-12","end_date":"1990-03-15","salary":"6544.78","city":"Vancouver","description":"Tester","department":"QA","active":"1"},
{"id":"4","first_name":"Celia","last_name":"Rice","start_date":"1982-10-24","end_date":"1999-04-21","salary":"2344.78","city":"Vancouver","description":"Manager","department":"HR","active":"1"},
{"id":"5","first_name":"Robert","last_name":"Black","start_date":"1984-01-15","end_date":"1998-08-08","salary":"2334.78","city":"Vancouver","description":"Tester","department":"IT","active":"1"},
{"id":"6","first_name":"Linda","last_name":"Green","start_date":"1987-07-30","end_date":"1996-01-04","salary":"4322.78","city":"New York","description":"Tester","department":"QA","active":"1"},
{"id":"7","first_name":"David","last_name":"Larry","start_date":"1990-12-31","end_date":"1998-02-12","salary":"7897.78","city":"New York","description":"Manager","department":"HR","active":"1"}
]
I need to call the function like this:
nest(data,["city","description","department"])
The first parameter is the entire dataset, the second is an array of columns which define the nesting level.
Expected JSON output:
[
{key: "city", value: "Toronto", count: 1, children:
[
{key: "description", value: "Programmer", count: 1, children:
[
{key: "department", value: "Finance", count: 1}
]
}
]
},
{key: "city", value: "Vancouver", count: 2, children:
[
{key: "description", value: "Tester", count: 3, children:
[
{key: "department", value: "Finance", count: 1},
{key: "department", value: "QA", count: 1},
{key: "department", value: "IT", count: 1}
]
},
{key: "description", value: "Manager", count: 1}
]
},
{key: "city", value: "New York", count: 2, children:
[
{key: "description", value: "Tester", count: 1, children:
[
{key: "department", value: "QA", count: 1}
]
},
{key: "description", value: "Manager", count: 1, children:
[
{key: "department", value: "HR", count: 1}
]
}
]
}
]
I've tried writing a few recursive functions but keep getting stuck when I have to dynamically search the tree to avoid duplication.
javascript
add a comment |
Original JSON data (flat table):
[
{"id":"1","first_name":"Jason","last_name":"Martin","start_date":"1996-07-25","end_date":"2006-07-25","salary":"1234.56","city":"Toronto","description":"Programmer","department":"Finance","active":"1"},
{"id":"2","first_name":"Alison","last_name":"Mathews","start_date":"1976-03-21","end_date":"1986-02-21","salary":"6661.78","city":"Vancouver","description":"Tester","department":"Finance","active":"1"},
{"id":"3","first_name":"James","last_name":"Smith","start_date":"1978-12-12","end_date":"1990-03-15","salary":"6544.78","city":"Vancouver","description":"Tester","department":"QA","active":"1"},
{"id":"4","first_name":"Celia","last_name":"Rice","start_date":"1982-10-24","end_date":"1999-04-21","salary":"2344.78","city":"Vancouver","description":"Manager","department":"HR","active":"1"},
{"id":"5","first_name":"Robert","last_name":"Black","start_date":"1984-01-15","end_date":"1998-08-08","salary":"2334.78","city":"Vancouver","description":"Tester","department":"IT","active":"1"},
{"id":"6","first_name":"Linda","last_name":"Green","start_date":"1987-07-30","end_date":"1996-01-04","salary":"4322.78","city":"New York","description":"Tester","department":"QA","active":"1"},
{"id":"7","first_name":"David","last_name":"Larry","start_date":"1990-12-31","end_date":"1998-02-12","salary":"7897.78","city":"New York","description":"Manager","department":"HR","active":"1"}
]
I need to call the function like this:
nest(data,["city","description","department"])
The first parameter is the entire dataset, the second is an array of columns which define the nesting level.
Expected JSON output:
[
{key: "city", value: "Toronto", count: 1, children:
[
{key: "description", value: "Programmer", count: 1, children:
[
{key: "department", value: "Finance", count: 1}
]
}
]
},
{key: "city", value: "Vancouver", count: 2, children:
[
{key: "description", value: "Tester", count: 3, children:
[
{key: "department", value: "Finance", count: 1},
{key: "department", value: "QA", count: 1},
{key: "department", value: "IT", count: 1}
]
},
{key: "description", value: "Manager", count: 1}
]
},
{key: "city", value: "New York", count: 2, children:
[
{key: "description", value: "Tester", count: 1, children:
[
{key: "department", value: "QA", count: 1}
]
},
{key: "description", value: "Manager", count: 1, children:
[
{key: "department", value: "HR", count: 1}
]
}
]
}
]
I've tried writing a few recursive functions but keep getting stuck when I have to dynamically search the tree to avoid duplication.
javascript
1
Why{key: "department", value: "Finance", count: 1}
instead of{key: "department", value: "Finance", count: 0}
? That node has no children.
– rob mayoff
Nov 29 '11 at 21:58
Welcome to Stack Overflow; please read the faq.
– zzzzBov
Nov 29 '11 at 22:01
What is count? It is 1 even when children is zero?
– goat
Nov 29 '11 at 22:14
@esskar - I currently have a recursive function that doesn't work because I would need a sort of xpath syntax to check if an element already exists in the tree
– WeaponX86
Nov 30 '11 at 4:12
@zzzzBov - Sorry if this question seemed convoluted, I can post the broken code I have
– WeaponX86
Nov 30 '11 at 4:23
add a comment |
Original JSON data (flat table):
[
{"id":"1","first_name":"Jason","last_name":"Martin","start_date":"1996-07-25","end_date":"2006-07-25","salary":"1234.56","city":"Toronto","description":"Programmer","department":"Finance","active":"1"},
{"id":"2","first_name":"Alison","last_name":"Mathews","start_date":"1976-03-21","end_date":"1986-02-21","salary":"6661.78","city":"Vancouver","description":"Tester","department":"Finance","active":"1"},
{"id":"3","first_name":"James","last_name":"Smith","start_date":"1978-12-12","end_date":"1990-03-15","salary":"6544.78","city":"Vancouver","description":"Tester","department":"QA","active":"1"},
{"id":"4","first_name":"Celia","last_name":"Rice","start_date":"1982-10-24","end_date":"1999-04-21","salary":"2344.78","city":"Vancouver","description":"Manager","department":"HR","active":"1"},
{"id":"5","first_name":"Robert","last_name":"Black","start_date":"1984-01-15","end_date":"1998-08-08","salary":"2334.78","city":"Vancouver","description":"Tester","department":"IT","active":"1"},
{"id":"6","first_name":"Linda","last_name":"Green","start_date":"1987-07-30","end_date":"1996-01-04","salary":"4322.78","city":"New York","description":"Tester","department":"QA","active":"1"},
{"id":"7","first_name":"David","last_name":"Larry","start_date":"1990-12-31","end_date":"1998-02-12","salary":"7897.78","city":"New York","description":"Manager","department":"HR","active":"1"}
]
I need to call the function like this:
nest(data,["city","description","department"])
The first parameter is the entire dataset, the second is an array of columns which define the nesting level.
Expected JSON output:
[
{key: "city", value: "Toronto", count: 1, children:
[
{key: "description", value: "Programmer", count: 1, children:
[
{key: "department", value: "Finance", count: 1}
]
}
]
},
{key: "city", value: "Vancouver", count: 2, children:
[
{key: "description", value: "Tester", count: 3, children:
[
{key: "department", value: "Finance", count: 1},
{key: "department", value: "QA", count: 1},
{key: "department", value: "IT", count: 1}
]
},
{key: "description", value: "Manager", count: 1}
]
},
{key: "city", value: "New York", count: 2, children:
[
{key: "description", value: "Tester", count: 1, children:
[
{key: "department", value: "QA", count: 1}
]
},
{key: "description", value: "Manager", count: 1, children:
[
{key: "department", value: "HR", count: 1}
]
}
]
}
]
I've tried writing a few recursive functions but keep getting stuck when I have to dynamically search the tree to avoid duplication.
javascript
Original JSON data (flat table):
[
{"id":"1","first_name":"Jason","last_name":"Martin","start_date":"1996-07-25","end_date":"2006-07-25","salary":"1234.56","city":"Toronto","description":"Programmer","department":"Finance","active":"1"},
{"id":"2","first_name":"Alison","last_name":"Mathews","start_date":"1976-03-21","end_date":"1986-02-21","salary":"6661.78","city":"Vancouver","description":"Tester","department":"Finance","active":"1"},
{"id":"3","first_name":"James","last_name":"Smith","start_date":"1978-12-12","end_date":"1990-03-15","salary":"6544.78","city":"Vancouver","description":"Tester","department":"QA","active":"1"},
{"id":"4","first_name":"Celia","last_name":"Rice","start_date":"1982-10-24","end_date":"1999-04-21","salary":"2344.78","city":"Vancouver","description":"Manager","department":"HR","active":"1"},
{"id":"5","first_name":"Robert","last_name":"Black","start_date":"1984-01-15","end_date":"1998-08-08","salary":"2334.78","city":"Vancouver","description":"Tester","department":"IT","active":"1"},
{"id":"6","first_name":"Linda","last_name":"Green","start_date":"1987-07-30","end_date":"1996-01-04","salary":"4322.78","city":"New York","description":"Tester","department":"QA","active":"1"},
{"id":"7","first_name":"David","last_name":"Larry","start_date":"1990-12-31","end_date":"1998-02-12","salary":"7897.78","city":"New York","description":"Manager","department":"HR","active":"1"}
]
I need to call the function like this:
nest(data,["city","description","department"])
The first parameter is the entire dataset, the second is an array of columns which define the nesting level.
Expected JSON output:
[
{key: "city", value: "Toronto", count: 1, children:
[
{key: "description", value: "Programmer", count: 1, children:
[
{key: "department", value: "Finance", count: 1}
]
}
]
},
{key: "city", value: "Vancouver", count: 2, children:
[
{key: "description", value: "Tester", count: 3, children:
[
{key: "department", value: "Finance", count: 1},
{key: "department", value: "QA", count: 1},
{key: "department", value: "IT", count: 1}
]
},
{key: "description", value: "Manager", count: 1}
]
},
{key: "city", value: "New York", count: 2, children:
[
{key: "description", value: "Tester", count: 1, children:
[
{key: "department", value: "QA", count: 1}
]
},
{key: "description", value: "Manager", count: 1, children:
[
{key: "department", value: "HR", count: 1}
]
}
]
}
]
I've tried writing a few recursive functions but keep getting stuck when I have to dynamically search the tree to avoid duplication.
javascript
javascript
edited Nov 29 '11 at 21:59
Jasper
70.7k12132140
70.7k12132140
asked Nov 29 '11 at 21:44
WeaponX86WeaponX86
6517
6517
1
Why{key: "department", value: "Finance", count: 1}
instead of{key: "department", value: "Finance", count: 0}
? That node has no children.
– rob mayoff
Nov 29 '11 at 21:58
Welcome to Stack Overflow; please read the faq.
– zzzzBov
Nov 29 '11 at 22:01
What is count? It is 1 even when children is zero?
– goat
Nov 29 '11 at 22:14
@esskar - I currently have a recursive function that doesn't work because I would need a sort of xpath syntax to check if an element already exists in the tree
– WeaponX86
Nov 30 '11 at 4:12
@zzzzBov - Sorry if this question seemed convoluted, I can post the broken code I have
– WeaponX86
Nov 30 '11 at 4:23
add a comment |
1
Why{key: "department", value: "Finance", count: 1}
instead of{key: "department", value: "Finance", count: 0}
? That node has no children.
– rob mayoff
Nov 29 '11 at 21:58
Welcome to Stack Overflow; please read the faq.
– zzzzBov
Nov 29 '11 at 22:01
What is count? It is 1 even when children is zero?
– goat
Nov 29 '11 at 22:14
@esskar - I currently have a recursive function that doesn't work because I would need a sort of xpath syntax to check if an element already exists in the tree
– WeaponX86
Nov 30 '11 at 4:12
@zzzzBov - Sorry if this question seemed convoluted, I can post the broken code I have
– WeaponX86
Nov 30 '11 at 4:23
1
1
Why
{key: "department", value: "Finance", count: 1}
instead of {key: "department", value: "Finance", count: 0}
? That node has no children.– rob mayoff
Nov 29 '11 at 21:58
Why
{key: "department", value: "Finance", count: 1}
instead of {key: "department", value: "Finance", count: 0}
? That node has no children.– rob mayoff
Nov 29 '11 at 21:58
Welcome to Stack Overflow; please read the faq.
– zzzzBov
Nov 29 '11 at 22:01
Welcome to Stack Overflow; please read the faq.
– zzzzBov
Nov 29 '11 at 22:01
What is count? It is 1 even when children is zero?
– goat
Nov 29 '11 at 22:14
What is count? It is 1 even when children is zero?
– goat
Nov 29 '11 at 22:14
@esskar - I currently have a recursive function that doesn't work because I would need a sort of xpath syntax to check if an element already exists in the tree
– WeaponX86
Nov 30 '11 at 4:12
@esskar - I currently have a recursive function that doesn't work because I would need a sort of xpath syntax to check if an element already exists in the tree
– WeaponX86
Nov 30 '11 at 4:12
@zzzzBov - Sorry if this question seemed convoluted, I can post the broken code I have
– WeaponX86
Nov 30 '11 at 4:23
@zzzzBov - Sorry if this question seemed convoluted, I can post the broken code I have
– WeaponX86
Nov 30 '11 at 4:23
add a comment |
3 Answers
3
active
oldest
votes
Thought this was a fun little question, so I did it... but, I do agree with the people who asked "what have you tried so far". Typically, you should talk about a specific problem.
// Groups a flat array into a tree.
// "data" is the flat array.
// "keys" is an array of properties to group on.
function groupBy(data, keys) {
if (keys.length == 0) return data;
// The current key to perform the grouping on:
var key = keys[0];
// Loop through the data and construct buckets for
// all of the unique keys:
var groups = {};
for (var i = 0; i < data.length; i++)
{
var row = data[i];
var groupValue = row[key];
if (groups[groupValue] == undefined)
{
groups[groupValue] = new Array();
}
groups[groupValue].push(row);
}
// Remove the first element from the groups array:
keys.reverse();
keys.pop()
keys.reverse();
// If there are no more keys left, we're done:
if (keys.length == 0) return groups;
// Otherwise, handle further groupings:
for (var group in groups)
{
groups[group] = groupBy(groups[group], keys.slice());
}
return groups;
}
Call the method like this:
var groupedData = groupBy(data, ["city","description","department"]);
The output from this method for your data looks like this:
{
"Toronto": {
"Programmer": {
"Finance": [
{
"id": "1", "first_name": "Jason", "last_name": "Martin", "start_date": "1996-07-25", "end_date": "2006-07-25", "salary": "1234.56", "city": "Toronto", "description": "Programmer", "department": "Finance", "active": "1"
}
]
}
},
"Vancouver": {
"Tester": {
"Finance": [
{
"id": "2", "first_name": "Alison", "last_name": "Mathews", "start_date": "1976-03-21", "end_date": "1986-02-21", "salary": "6661.78", "city": "Vancouver", "description": "Tester", "department": "Finance", "active": "1"
}
],
"QA": [
{
"id": "3", "first_name": "James", "last_name": "Smith", "start_date": "1978-12-12", "end_date": "1990-03-15", "salary": "6544.78", "city": "Vancouver", "description": "Tester", "department": "QA", "active": "1"
}
],
"IT": [
{
"id": "5", "first_name": "Robert", "last_name": "Black", "start_date": "1984-01-15", "end_date": "1998-08-08", "salary": "2334.78", "city": "Vancouver", "description": "Tester", "department": "IT", "active": "1"
}
]
},
"Manager": {
"HR": [
{
"id": "4", "first_name": "Celia", "last_name": "Rice", "start_date": "1982-10-24", "end_date": "1999-04-21", "salary": "2344.78", "city": "Vancouver", "description": "Manager", "department": "HR", "active": "1"
}
]
}
},
"New York": {
"Tester": {
"QA": [
{
"id": "6", "first_name": "Linda", "last_name": "Green", "start_date": "1987-07-30", "end_date": "1996-01-04", "salary": "4322.78", "city": "New York", "description": "Tester", "department": "QA", "active": "1"
}
]
},
"Manager": {
"HR": [
{
"id": "7", "first_name": "David", "last_name": "Larry", "start_date": "1990-12-31", "end_date": "1998-02-12", "salary": "7897.78", "city": "New York", "description": "Manager", "department": "HR", "active": "1"
}
]
}
}
}
Because the groups are all javascript objects, you don't need that "count" member. You can simply use the .length property of the array(s).
Loop through the groups using javascript's for (var group in groups)
syntax.
1
That was really helpful, thank you.
– WeaponX86
Nov 30 '11 at 4:19
A nice solution, saving for later.
– Oisin Lavery
Aug 12 '13 at 10:40
add a comment |
You might take a look at the nest()
operator from D3.js:
https://github.com/mbostock/d3/blob/48ad44fdeef32b518c6271bb99a9aed376c1a1d6/src/arrays/nest.js
This is part of D3, a larger library, but looking quickly at the code I just linked to, I don't think this has any dependencies, so you should be able to lift the code here for use in your own project. Usage is described here in the docs - you chain .key()
methods to define the keys for each layer of the nested structure. In your case, this might look like:
data = d3.nest()
.key(function(d) { return d.city })
.key(function(d) { return d.description })
.entries(data);
The structure this spits out is a little different from what you have, but it's functionally quite similar:
[
{
"key": "Toronto",
"values": [
{
"key": "Programmer",
"values": [
{
"active": "1",
"city": "Toronto",
"department": "Finance",
"description": "Programmer",
"end_date": "2006-07-25",
"first_name": "Jason",
"id": "1",
"last_name": "Martin",
"salary": "1234.56",
"start_date": "1996-07-25"
},
// etc ...
]
}
]
},
// etc ...
]
+1 Good one... has a couple benefits over the quick-and-dirty version I put together, including; developer-selectable keys (meaning it would be easy to make the keys case-insensitive or calculated), and sorting. Personally, I'd probably tweak my version to add those features instead of brining in (yet another) library... but nice find!
– Steve
Nov 29 '11 at 23:02
1
Thanks - you don't need the whole library, just the function in the code I linked to, which doesn't have any D3 dependencies. But it's true that it might be overkill for some projects.
– nrabinowitz
Nov 29 '11 at 23:45
I will definitely take a look at this, nice find!
– WeaponX86
Nov 30 '11 at 4:20
Awesome this is exactly what I needed. Definitely the cleanest approach possible.
– parliament
Nov 28 '17 at 22:43
add a comment |
Building on the example provided by @nrabinowitz, here's the nest function with the originally proposed API of passing the collection and an array of property names as args, using d3.nest under the hood:
function nest(data, keys) {
var nest = d3.nest();
keys.forEach(function(k) {
nest.key(function(d) {
return d[k];
})
});
return nest.entries(data);
}
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f8318150%2fconvert-flat-array-of-objects-into-nested-array-of-objects%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thought this was a fun little question, so I did it... but, I do agree with the people who asked "what have you tried so far". Typically, you should talk about a specific problem.
// Groups a flat array into a tree.
// "data" is the flat array.
// "keys" is an array of properties to group on.
function groupBy(data, keys) {
if (keys.length == 0) return data;
// The current key to perform the grouping on:
var key = keys[0];
// Loop through the data and construct buckets for
// all of the unique keys:
var groups = {};
for (var i = 0; i < data.length; i++)
{
var row = data[i];
var groupValue = row[key];
if (groups[groupValue] == undefined)
{
groups[groupValue] = new Array();
}
groups[groupValue].push(row);
}
// Remove the first element from the groups array:
keys.reverse();
keys.pop()
keys.reverse();
// If there are no more keys left, we're done:
if (keys.length == 0) return groups;
// Otherwise, handle further groupings:
for (var group in groups)
{
groups[group] = groupBy(groups[group], keys.slice());
}
return groups;
}
Call the method like this:
var groupedData = groupBy(data, ["city","description","department"]);
The output from this method for your data looks like this:
{
"Toronto": {
"Programmer": {
"Finance": [
{
"id": "1", "first_name": "Jason", "last_name": "Martin", "start_date": "1996-07-25", "end_date": "2006-07-25", "salary": "1234.56", "city": "Toronto", "description": "Programmer", "department": "Finance", "active": "1"
}
]
}
},
"Vancouver": {
"Tester": {
"Finance": [
{
"id": "2", "first_name": "Alison", "last_name": "Mathews", "start_date": "1976-03-21", "end_date": "1986-02-21", "salary": "6661.78", "city": "Vancouver", "description": "Tester", "department": "Finance", "active": "1"
}
],
"QA": [
{
"id": "3", "first_name": "James", "last_name": "Smith", "start_date": "1978-12-12", "end_date": "1990-03-15", "salary": "6544.78", "city": "Vancouver", "description": "Tester", "department": "QA", "active": "1"
}
],
"IT": [
{
"id": "5", "first_name": "Robert", "last_name": "Black", "start_date": "1984-01-15", "end_date": "1998-08-08", "salary": "2334.78", "city": "Vancouver", "description": "Tester", "department": "IT", "active": "1"
}
]
},
"Manager": {
"HR": [
{
"id": "4", "first_name": "Celia", "last_name": "Rice", "start_date": "1982-10-24", "end_date": "1999-04-21", "salary": "2344.78", "city": "Vancouver", "description": "Manager", "department": "HR", "active": "1"
}
]
}
},
"New York": {
"Tester": {
"QA": [
{
"id": "6", "first_name": "Linda", "last_name": "Green", "start_date": "1987-07-30", "end_date": "1996-01-04", "salary": "4322.78", "city": "New York", "description": "Tester", "department": "QA", "active": "1"
}
]
},
"Manager": {
"HR": [
{
"id": "7", "first_name": "David", "last_name": "Larry", "start_date": "1990-12-31", "end_date": "1998-02-12", "salary": "7897.78", "city": "New York", "description": "Manager", "department": "HR", "active": "1"
}
]
}
}
}
Because the groups are all javascript objects, you don't need that "count" member. You can simply use the .length property of the array(s).
Loop through the groups using javascript's for (var group in groups)
syntax.
1
That was really helpful, thank you.
– WeaponX86
Nov 30 '11 at 4:19
A nice solution, saving for later.
– Oisin Lavery
Aug 12 '13 at 10:40
add a comment |
Thought this was a fun little question, so I did it... but, I do agree with the people who asked "what have you tried so far". Typically, you should talk about a specific problem.
// Groups a flat array into a tree.
// "data" is the flat array.
// "keys" is an array of properties to group on.
function groupBy(data, keys) {
if (keys.length == 0) return data;
// The current key to perform the grouping on:
var key = keys[0];
// Loop through the data and construct buckets for
// all of the unique keys:
var groups = {};
for (var i = 0; i < data.length; i++)
{
var row = data[i];
var groupValue = row[key];
if (groups[groupValue] == undefined)
{
groups[groupValue] = new Array();
}
groups[groupValue].push(row);
}
// Remove the first element from the groups array:
keys.reverse();
keys.pop()
keys.reverse();
// If there are no more keys left, we're done:
if (keys.length == 0) return groups;
// Otherwise, handle further groupings:
for (var group in groups)
{
groups[group] = groupBy(groups[group], keys.slice());
}
return groups;
}
Call the method like this:
var groupedData = groupBy(data, ["city","description","department"]);
The output from this method for your data looks like this:
{
"Toronto": {
"Programmer": {
"Finance": [
{
"id": "1", "first_name": "Jason", "last_name": "Martin", "start_date": "1996-07-25", "end_date": "2006-07-25", "salary": "1234.56", "city": "Toronto", "description": "Programmer", "department": "Finance", "active": "1"
}
]
}
},
"Vancouver": {
"Tester": {
"Finance": [
{
"id": "2", "first_name": "Alison", "last_name": "Mathews", "start_date": "1976-03-21", "end_date": "1986-02-21", "salary": "6661.78", "city": "Vancouver", "description": "Tester", "department": "Finance", "active": "1"
}
],
"QA": [
{
"id": "3", "first_name": "James", "last_name": "Smith", "start_date": "1978-12-12", "end_date": "1990-03-15", "salary": "6544.78", "city": "Vancouver", "description": "Tester", "department": "QA", "active": "1"
}
],
"IT": [
{
"id": "5", "first_name": "Robert", "last_name": "Black", "start_date": "1984-01-15", "end_date": "1998-08-08", "salary": "2334.78", "city": "Vancouver", "description": "Tester", "department": "IT", "active": "1"
}
]
},
"Manager": {
"HR": [
{
"id": "4", "first_name": "Celia", "last_name": "Rice", "start_date": "1982-10-24", "end_date": "1999-04-21", "salary": "2344.78", "city": "Vancouver", "description": "Manager", "department": "HR", "active": "1"
}
]
}
},
"New York": {
"Tester": {
"QA": [
{
"id": "6", "first_name": "Linda", "last_name": "Green", "start_date": "1987-07-30", "end_date": "1996-01-04", "salary": "4322.78", "city": "New York", "description": "Tester", "department": "QA", "active": "1"
}
]
},
"Manager": {
"HR": [
{
"id": "7", "first_name": "David", "last_name": "Larry", "start_date": "1990-12-31", "end_date": "1998-02-12", "salary": "7897.78", "city": "New York", "description": "Manager", "department": "HR", "active": "1"
}
]
}
}
}
Because the groups are all javascript objects, you don't need that "count" member. You can simply use the .length property of the array(s).
Loop through the groups using javascript's for (var group in groups)
syntax.
1
That was really helpful, thank you.
– WeaponX86
Nov 30 '11 at 4:19
A nice solution, saving for later.
– Oisin Lavery
Aug 12 '13 at 10:40
add a comment |
Thought this was a fun little question, so I did it... but, I do agree with the people who asked "what have you tried so far". Typically, you should talk about a specific problem.
// Groups a flat array into a tree.
// "data" is the flat array.
// "keys" is an array of properties to group on.
function groupBy(data, keys) {
if (keys.length == 0) return data;
// The current key to perform the grouping on:
var key = keys[0];
// Loop through the data and construct buckets for
// all of the unique keys:
var groups = {};
for (var i = 0; i < data.length; i++)
{
var row = data[i];
var groupValue = row[key];
if (groups[groupValue] == undefined)
{
groups[groupValue] = new Array();
}
groups[groupValue].push(row);
}
// Remove the first element from the groups array:
keys.reverse();
keys.pop()
keys.reverse();
// If there are no more keys left, we're done:
if (keys.length == 0) return groups;
// Otherwise, handle further groupings:
for (var group in groups)
{
groups[group] = groupBy(groups[group], keys.slice());
}
return groups;
}
Call the method like this:
var groupedData = groupBy(data, ["city","description","department"]);
The output from this method for your data looks like this:
{
"Toronto": {
"Programmer": {
"Finance": [
{
"id": "1", "first_name": "Jason", "last_name": "Martin", "start_date": "1996-07-25", "end_date": "2006-07-25", "salary": "1234.56", "city": "Toronto", "description": "Programmer", "department": "Finance", "active": "1"
}
]
}
},
"Vancouver": {
"Tester": {
"Finance": [
{
"id": "2", "first_name": "Alison", "last_name": "Mathews", "start_date": "1976-03-21", "end_date": "1986-02-21", "salary": "6661.78", "city": "Vancouver", "description": "Tester", "department": "Finance", "active": "1"
}
],
"QA": [
{
"id": "3", "first_name": "James", "last_name": "Smith", "start_date": "1978-12-12", "end_date": "1990-03-15", "salary": "6544.78", "city": "Vancouver", "description": "Tester", "department": "QA", "active": "1"
}
],
"IT": [
{
"id": "5", "first_name": "Robert", "last_name": "Black", "start_date": "1984-01-15", "end_date": "1998-08-08", "salary": "2334.78", "city": "Vancouver", "description": "Tester", "department": "IT", "active": "1"
}
]
},
"Manager": {
"HR": [
{
"id": "4", "first_name": "Celia", "last_name": "Rice", "start_date": "1982-10-24", "end_date": "1999-04-21", "salary": "2344.78", "city": "Vancouver", "description": "Manager", "department": "HR", "active": "1"
}
]
}
},
"New York": {
"Tester": {
"QA": [
{
"id": "6", "first_name": "Linda", "last_name": "Green", "start_date": "1987-07-30", "end_date": "1996-01-04", "salary": "4322.78", "city": "New York", "description": "Tester", "department": "QA", "active": "1"
}
]
},
"Manager": {
"HR": [
{
"id": "7", "first_name": "David", "last_name": "Larry", "start_date": "1990-12-31", "end_date": "1998-02-12", "salary": "7897.78", "city": "New York", "description": "Manager", "department": "HR", "active": "1"
}
]
}
}
}
Because the groups are all javascript objects, you don't need that "count" member. You can simply use the .length property of the array(s).
Loop through the groups using javascript's for (var group in groups)
syntax.
Thought this was a fun little question, so I did it... but, I do agree with the people who asked "what have you tried so far". Typically, you should talk about a specific problem.
// Groups a flat array into a tree.
// "data" is the flat array.
// "keys" is an array of properties to group on.
function groupBy(data, keys) {
if (keys.length == 0) return data;
// The current key to perform the grouping on:
var key = keys[0];
// Loop through the data and construct buckets for
// all of the unique keys:
var groups = {};
for (var i = 0; i < data.length; i++)
{
var row = data[i];
var groupValue = row[key];
if (groups[groupValue] == undefined)
{
groups[groupValue] = new Array();
}
groups[groupValue].push(row);
}
// Remove the first element from the groups array:
keys.reverse();
keys.pop()
keys.reverse();
// If there are no more keys left, we're done:
if (keys.length == 0) return groups;
// Otherwise, handle further groupings:
for (var group in groups)
{
groups[group] = groupBy(groups[group], keys.slice());
}
return groups;
}
Call the method like this:
var groupedData = groupBy(data, ["city","description","department"]);
The output from this method for your data looks like this:
{
"Toronto": {
"Programmer": {
"Finance": [
{
"id": "1", "first_name": "Jason", "last_name": "Martin", "start_date": "1996-07-25", "end_date": "2006-07-25", "salary": "1234.56", "city": "Toronto", "description": "Programmer", "department": "Finance", "active": "1"
}
]
}
},
"Vancouver": {
"Tester": {
"Finance": [
{
"id": "2", "first_name": "Alison", "last_name": "Mathews", "start_date": "1976-03-21", "end_date": "1986-02-21", "salary": "6661.78", "city": "Vancouver", "description": "Tester", "department": "Finance", "active": "1"
}
],
"QA": [
{
"id": "3", "first_name": "James", "last_name": "Smith", "start_date": "1978-12-12", "end_date": "1990-03-15", "salary": "6544.78", "city": "Vancouver", "description": "Tester", "department": "QA", "active": "1"
}
],
"IT": [
{
"id": "5", "first_name": "Robert", "last_name": "Black", "start_date": "1984-01-15", "end_date": "1998-08-08", "salary": "2334.78", "city": "Vancouver", "description": "Tester", "department": "IT", "active": "1"
}
]
},
"Manager": {
"HR": [
{
"id": "4", "first_name": "Celia", "last_name": "Rice", "start_date": "1982-10-24", "end_date": "1999-04-21", "salary": "2344.78", "city": "Vancouver", "description": "Manager", "department": "HR", "active": "1"
}
]
}
},
"New York": {
"Tester": {
"QA": [
{
"id": "6", "first_name": "Linda", "last_name": "Green", "start_date": "1987-07-30", "end_date": "1996-01-04", "salary": "4322.78", "city": "New York", "description": "Tester", "department": "QA", "active": "1"
}
]
},
"Manager": {
"HR": [
{
"id": "7", "first_name": "David", "last_name": "Larry", "start_date": "1990-12-31", "end_date": "1998-02-12", "salary": "7897.78", "city": "New York", "description": "Manager", "department": "HR", "active": "1"
}
]
}
}
}
Because the groups are all javascript objects, you don't need that "count" member. You can simply use the .length property of the array(s).
Loop through the groups using javascript's for (var group in groups)
syntax.
edited Nov 29 '11 at 22:51
answered Nov 29 '11 at 22:13
SteveSteve
24.9k1389116
24.9k1389116
1
That was really helpful, thank you.
– WeaponX86
Nov 30 '11 at 4:19
A nice solution, saving for later.
– Oisin Lavery
Aug 12 '13 at 10:40
add a comment |
1
That was really helpful, thank you.
– WeaponX86
Nov 30 '11 at 4:19
A nice solution, saving for later.
– Oisin Lavery
Aug 12 '13 at 10:40
1
1
That was really helpful, thank you.
– WeaponX86
Nov 30 '11 at 4:19
That was really helpful, thank you.
– WeaponX86
Nov 30 '11 at 4:19
A nice solution, saving for later.
– Oisin Lavery
Aug 12 '13 at 10:40
A nice solution, saving for later.
– Oisin Lavery
Aug 12 '13 at 10:40
add a comment |
You might take a look at the nest()
operator from D3.js:
https://github.com/mbostock/d3/blob/48ad44fdeef32b518c6271bb99a9aed376c1a1d6/src/arrays/nest.js
This is part of D3, a larger library, but looking quickly at the code I just linked to, I don't think this has any dependencies, so you should be able to lift the code here for use in your own project. Usage is described here in the docs - you chain .key()
methods to define the keys for each layer of the nested structure. In your case, this might look like:
data = d3.nest()
.key(function(d) { return d.city })
.key(function(d) { return d.description })
.entries(data);
The structure this spits out is a little different from what you have, but it's functionally quite similar:
[
{
"key": "Toronto",
"values": [
{
"key": "Programmer",
"values": [
{
"active": "1",
"city": "Toronto",
"department": "Finance",
"description": "Programmer",
"end_date": "2006-07-25",
"first_name": "Jason",
"id": "1",
"last_name": "Martin",
"salary": "1234.56",
"start_date": "1996-07-25"
},
// etc ...
]
}
]
},
// etc ...
]
+1 Good one... has a couple benefits over the quick-and-dirty version I put together, including; developer-selectable keys (meaning it would be easy to make the keys case-insensitive or calculated), and sorting. Personally, I'd probably tweak my version to add those features instead of brining in (yet another) library... but nice find!
– Steve
Nov 29 '11 at 23:02
1
Thanks - you don't need the whole library, just the function in the code I linked to, which doesn't have any D3 dependencies. But it's true that it might be overkill for some projects.
– nrabinowitz
Nov 29 '11 at 23:45
I will definitely take a look at this, nice find!
– WeaponX86
Nov 30 '11 at 4:20
Awesome this is exactly what I needed. Definitely the cleanest approach possible.
– parliament
Nov 28 '17 at 22:43
add a comment |
You might take a look at the nest()
operator from D3.js:
https://github.com/mbostock/d3/blob/48ad44fdeef32b518c6271bb99a9aed376c1a1d6/src/arrays/nest.js
This is part of D3, a larger library, but looking quickly at the code I just linked to, I don't think this has any dependencies, so you should be able to lift the code here for use in your own project. Usage is described here in the docs - you chain .key()
methods to define the keys for each layer of the nested structure. In your case, this might look like:
data = d3.nest()
.key(function(d) { return d.city })
.key(function(d) { return d.description })
.entries(data);
The structure this spits out is a little different from what you have, but it's functionally quite similar:
[
{
"key": "Toronto",
"values": [
{
"key": "Programmer",
"values": [
{
"active": "1",
"city": "Toronto",
"department": "Finance",
"description": "Programmer",
"end_date": "2006-07-25",
"first_name": "Jason",
"id": "1",
"last_name": "Martin",
"salary": "1234.56",
"start_date": "1996-07-25"
},
// etc ...
]
}
]
},
// etc ...
]
+1 Good one... has a couple benefits over the quick-and-dirty version I put together, including; developer-selectable keys (meaning it would be easy to make the keys case-insensitive or calculated), and sorting. Personally, I'd probably tweak my version to add those features instead of brining in (yet another) library... but nice find!
– Steve
Nov 29 '11 at 23:02
1
Thanks - you don't need the whole library, just the function in the code I linked to, which doesn't have any D3 dependencies. But it's true that it might be overkill for some projects.
– nrabinowitz
Nov 29 '11 at 23:45
I will definitely take a look at this, nice find!
– WeaponX86
Nov 30 '11 at 4:20
Awesome this is exactly what I needed. Definitely the cleanest approach possible.
– parliament
Nov 28 '17 at 22:43
add a comment |
You might take a look at the nest()
operator from D3.js:
https://github.com/mbostock/d3/blob/48ad44fdeef32b518c6271bb99a9aed376c1a1d6/src/arrays/nest.js
This is part of D3, a larger library, but looking quickly at the code I just linked to, I don't think this has any dependencies, so you should be able to lift the code here for use in your own project. Usage is described here in the docs - you chain .key()
methods to define the keys for each layer of the nested structure. In your case, this might look like:
data = d3.nest()
.key(function(d) { return d.city })
.key(function(d) { return d.description })
.entries(data);
The structure this spits out is a little different from what you have, but it's functionally quite similar:
[
{
"key": "Toronto",
"values": [
{
"key": "Programmer",
"values": [
{
"active": "1",
"city": "Toronto",
"department": "Finance",
"description": "Programmer",
"end_date": "2006-07-25",
"first_name": "Jason",
"id": "1",
"last_name": "Martin",
"salary": "1234.56",
"start_date": "1996-07-25"
},
// etc ...
]
}
]
},
// etc ...
]
You might take a look at the nest()
operator from D3.js:
https://github.com/mbostock/d3/blob/48ad44fdeef32b518c6271bb99a9aed376c1a1d6/src/arrays/nest.js
This is part of D3, a larger library, but looking quickly at the code I just linked to, I don't think this has any dependencies, so you should be able to lift the code here for use in your own project. Usage is described here in the docs - you chain .key()
methods to define the keys for each layer of the nested structure. In your case, this might look like:
data = d3.nest()
.key(function(d) { return d.city })
.key(function(d) { return d.description })
.entries(data);
The structure this spits out is a little different from what you have, but it's functionally quite similar:
[
{
"key": "Toronto",
"values": [
{
"key": "Programmer",
"values": [
{
"active": "1",
"city": "Toronto",
"department": "Finance",
"description": "Programmer",
"end_date": "2006-07-25",
"first_name": "Jason",
"id": "1",
"last_name": "Martin",
"salary": "1234.56",
"start_date": "1996-07-25"
},
// etc ...
]
}
]
},
// etc ...
]
edited Aug 20 '14 at 20:57
user2357169
6719
6719
answered Nov 29 '11 at 22:55
nrabinowitznrabinowitz
48.1k8131151
48.1k8131151
+1 Good one... has a couple benefits over the quick-and-dirty version I put together, including; developer-selectable keys (meaning it would be easy to make the keys case-insensitive or calculated), and sorting. Personally, I'd probably tweak my version to add those features instead of brining in (yet another) library... but nice find!
– Steve
Nov 29 '11 at 23:02
1
Thanks - you don't need the whole library, just the function in the code I linked to, which doesn't have any D3 dependencies. But it's true that it might be overkill for some projects.
– nrabinowitz
Nov 29 '11 at 23:45
I will definitely take a look at this, nice find!
– WeaponX86
Nov 30 '11 at 4:20
Awesome this is exactly what I needed. Definitely the cleanest approach possible.
– parliament
Nov 28 '17 at 22:43
add a comment |
+1 Good one... has a couple benefits over the quick-and-dirty version I put together, including; developer-selectable keys (meaning it would be easy to make the keys case-insensitive or calculated), and sorting. Personally, I'd probably tweak my version to add those features instead of brining in (yet another) library... but nice find!
– Steve
Nov 29 '11 at 23:02
1
Thanks - you don't need the whole library, just the function in the code I linked to, which doesn't have any D3 dependencies. But it's true that it might be overkill for some projects.
– nrabinowitz
Nov 29 '11 at 23:45
I will definitely take a look at this, nice find!
– WeaponX86
Nov 30 '11 at 4:20
Awesome this is exactly what I needed. Definitely the cleanest approach possible.
– parliament
Nov 28 '17 at 22:43
+1 Good one... has a couple benefits over the quick-and-dirty version I put together, including; developer-selectable keys (meaning it would be easy to make the keys case-insensitive or calculated), and sorting. Personally, I'd probably tweak my version to add those features instead of brining in (yet another) library... but nice find!
– Steve
Nov 29 '11 at 23:02
+1 Good one... has a couple benefits over the quick-and-dirty version I put together, including; developer-selectable keys (meaning it would be easy to make the keys case-insensitive or calculated), and sorting. Personally, I'd probably tweak my version to add those features instead of brining in (yet another) library... but nice find!
– Steve
Nov 29 '11 at 23:02
1
1
Thanks - you don't need the whole library, just the function in the code I linked to, which doesn't have any D3 dependencies. But it's true that it might be overkill for some projects.
– nrabinowitz
Nov 29 '11 at 23:45
Thanks - you don't need the whole library, just the function in the code I linked to, which doesn't have any D3 dependencies. But it's true that it might be overkill for some projects.
– nrabinowitz
Nov 29 '11 at 23:45
I will definitely take a look at this, nice find!
– WeaponX86
Nov 30 '11 at 4:20
I will definitely take a look at this, nice find!
– WeaponX86
Nov 30 '11 at 4:20
Awesome this is exactly what I needed. Definitely the cleanest approach possible.
– parliament
Nov 28 '17 at 22:43
Awesome this is exactly what I needed. Definitely the cleanest approach possible.
– parliament
Nov 28 '17 at 22:43
add a comment |
Building on the example provided by @nrabinowitz, here's the nest function with the originally proposed API of passing the collection and an array of property names as args, using d3.nest under the hood:
function nest(data, keys) {
var nest = d3.nest();
keys.forEach(function(k) {
nest.key(function(d) {
return d[k];
})
});
return nest.entries(data);
}
add a comment |
Building on the example provided by @nrabinowitz, here's the nest function with the originally proposed API of passing the collection and an array of property names as args, using d3.nest under the hood:
function nest(data, keys) {
var nest = d3.nest();
keys.forEach(function(k) {
nest.key(function(d) {
return d[k];
})
});
return nest.entries(data);
}
add a comment |
Building on the example provided by @nrabinowitz, here's the nest function with the originally proposed API of passing the collection and an array of property names as args, using d3.nest under the hood:
function nest(data, keys) {
var nest = d3.nest();
keys.forEach(function(k) {
nest.key(function(d) {
return d[k];
})
});
return nest.entries(data);
}
Building on the example provided by @nrabinowitz, here's the nest function with the originally proposed API of passing the collection and an array of property names as args, using d3.nest under the hood:
function nest(data, keys) {
var nest = d3.nest();
keys.forEach(function(k) {
nest.key(function(d) {
return d[k];
})
});
return nest.entries(data);
}
edited Dec 8 '16 at 20:38
answered Dec 8 '16 at 16:13
EricEric
727612
727612
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f8318150%2fconvert-flat-array-of-objects-into-nested-array-of-objects%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Why
{key: "department", value: "Finance", count: 1}
instead of{key: "department", value: "Finance", count: 0}
? That node has no children.– rob mayoff
Nov 29 '11 at 21:58
Welcome to Stack Overflow; please read the faq.
– zzzzBov
Nov 29 '11 at 22:01
What is count? It is 1 even when children is zero?
– goat
Nov 29 '11 at 22:14
@esskar - I currently have a recursive function that doesn't work because I would need a sort of xpath syntax to check if an element already exists in the tree
– WeaponX86
Nov 30 '11 at 4:12
@zzzzBov - Sorry if this question seemed convoluted, I can post the broken code I have
– WeaponX86
Nov 30 '11 at 4:23