Arrange JSON Array to given Hierarchy group without Parent ID - javascript

I have an Array of Key names (let's call it 'Labels') and another array of Objects (let's call it 'Data'). I am trying to convert Data Array into Parent Child structure by using the hierarchy of Labels Array. That means, First Data will be grouped by "Gender" (parent) and then "Age" (children of Gender) and then "ID" (children of Age)
//.... means it can be any number of items
var Labels = ["Gender", "Age", "ID", ....];
var Data = [{
"Age":"22",
"Gender":"Male",
"ID":"A111"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A113"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A109"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A115"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A105"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A107"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A103"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A101"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A114"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A112"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A108"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A104"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A106"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A102"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A110"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A111"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A113"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A109"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A115"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A105"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A107"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A103"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A101"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A114"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A112"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A108"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A104"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A106"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A102"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A110"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A111"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A113"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A109"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A115"
},
{
"Age":"22",
"Gender":"Male",
"ID":"A105"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A107"
},
{
"Age":"23",
"Gender":"Male",
"ID":"A103"
},
{
"Age":"21",
"Gender":"Male",
"ID":"A101"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A114"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A112"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A108"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A104"
},
{
"Age":"23",
"Gender":"Female",
"ID":"A106"
},
{
"Age":"22",
"Gender":"Female",
"ID":"A102"
},
{
"Age":"21",
"Gender":"Female",
"ID":"A110"
}]
Expected Output of above Data should be:
var output = [{
name: "Male",
childrens:[
{
name: "21",
childrens: [{name: "Al01"},{name: "Al07"}, ....]
},
{
name: "22",
childrens: [{name: "A111"},{name: "A113"}, ....]
},
{
name: "23",
childrens: [{name: "A109"},{name: "A115"}, ....]
}
]
},
{
name: "Female",
childrens:[
{
name: "21",
childrens: [{name: "Al04"},{name: "Al10"}, ....]
},
{
name: "22",
childrens: [{name: "A102"},{name: "A114"}, ....]
},
{
name: "23",
childrens: [{name: "A106"},{name: "A109"}, ....]
}
]
}];
I've tried groupBy() as shown below But not getting the expected output.
var groupBy = function (xs, key) {
return xs.reduce(function (rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
function calcCats(ca, ss) {
let K2 = groupBy(ca, ss);
ca = [];
Object.keys(K2).forEach(cc => {
K2[cc].forEach(kk=>{
delete kk[ss];
});
let tv = { name: cc, type: ss };
if(Object.keys(K2[cc][0]).length > 0)
{
tv.categories= K2[cc];
}
ca.push(tv);
});
return ca;
}
var output = [];
Labels.forEach((ss, i) => {
if (output.length <= 0) {
let K1 = groupBy(output, ss);
Object.keys(K1).forEach(cc => {
output.push({ name: cc, categories: K1[cc] });
});
} else {
output.forEach(ca => {
ca.categories = calcCats(ca.categories, ss);
});
}
});

You could take an object which keeps the references to the groups for the actual level, and the array of the results of the level in a nested style and aff a new level from an array of keys.
Let us have a look to the object (_ and the rest properties are switched)
{
"Male": {
"21": {
"_": /**ref:1a**/
},
"22": {
"_": /**ref:6**/
},
"23": {
"_": /**ref:f**/
},
"_": /**ref:4**/
},
"Female": {
"21": {
"_": /**ref:39**/
},
"22": {
"_": /**ref:26**/
},
"23": {
"_": /**ref:31**/
},
"_": /**ref:24**/
},
"_": [
{
"name": "Male",
"children": [
/**id:4**/
{
"name": "22",
"children": [
/**id:6**/
{ "ID": "A111" },
{ "ID": "A113" },
// ...
]
},
{
"name": "23",
"children": [
/**id:f**/
{ "ID": "A109" },
{ "ID": "A115" },
// ...
]
},
{
"name": "21",
"children": [
/**id:1a**/
{ "ID": "A107" },
{ "ID": "A101" },
// ...
]
}
]
},
{
"name": "Female",
"children": [
/**id:24**/
{
"name": "22",
"children": [
/**id:26**/
{ "ID": "A114" },
{ "ID": "A108" },
// ...
]
},
{
"name": "23",
"children": [
/**id:31**/
{ "ID": "A112" },
{ "ID": "A106" },
// ...
]
},
{
"name": "21",
"children": [
/**id:39**/
{ "ID": "A104" },
{ "ID": "A110" },
// ...
]
}
]
}
]
}
The object contains nested groups of first level Gender with Male/Female and another level Age with their values and a reference to an array.
This is not the wanted structure for the result.
The result contains arrays of objects with a name property which reflects the level value and later an object without the grouping properties.
In depth:
The outer reduce iterates the given objects. As well as it keeps the object for each iteration and this allowes to use the object with a fast access to the nested groups.
The inner reduce takes a level and a key for this level and destructure the key from the object to get an object without the key for the actual level.
If the level does not have a property with the actual group value, then a new property with the name and an object with only one property _ is created with an array as value. the array contains all values of the level. The underscore is needed to prevent a conflict with other values of the grouping value.
Actually only the first structure is creater, this would not appear in the result set, right now. For having this array in the result set, it need to add an object with a reference to the array as children.
Finall the object with the level group is returned.
At the leaves the rest of the data object has to pushed to the array.
const
data = [{ Age: "22", Gender: "Male", ID: "A111" }, { Age: "22", Gender: "Male", ID: "A113" }, { Age: "23", Gender: "Male", ID: "A109" }, { Age: "23", Gender: "Male", ID: "A115" }, { Age: "22", Gender: "Male", ID: "A105" }, { Age: "21", Gender: "Male", ID: "A107" }, { Age: "23", Gender: "Male", ID: "A103" }, { Age: "21", Gender: "Male", ID: "A101" }, { Age: "22", Gender: "Female", ID: "A114" }, { Age: "23", Gender: "Female", ID: "A112" }, { Age: "22", Gender: "Female", ID: "A108" }, { Age: "21", Gender: "Female", ID: "A104" }, { Age: "23", Gender: "Female", ID: "A106" }, { Age: "22", Gender: "Female", ID: "A102" }, { Age: "21", Gender: "Female", ID: "A110" }, { Age: "22", Gender: "Male", ID: "A111" }, { Age: "21", Gender: "Male", ID: "A113" }, { Age: "23", Gender: "Male", ID: "A109" }, { Age: "23", Gender: "Male", ID: "A115" }, { Age: "22", Gender: "Male", ID: "A105" }, { Age: "21", Gender: "Male", ID: "A107" }, { Age: "23", Gender: "Male", ID: "A103" }, { Age: "21", Gender: "Male", ID: "A101" }, { Age: "22", Gender: "Female", ID: "A114" }, { Age: "23", Gender: "Female", ID: "A112" }, { Age: "22", Gender: "Female", ID: "A108" }, { Age: "21", Gender: "Female", ID: "A104" }, { Age: "23", Gender: "Female", ID: "A106" }, { Age: "22", Gender: "Female", ID: "A102" }, { Age: "21", Gender: "Female", ID: "A110" }, { Age: "22", Gender: "Male", ID: "A111" }, { Age: "21", Gender: "Male", ID: "A113" }, { Age: "23", Gender: "Male", ID: "A109" }, { Age: "23", Gender: "Male", ID: "A115" }, { Age: "22", Gender: "Male", ID: "A105" }, { Age: "21", Gender: "Male", ID: "A107" }, { Age: "23", Gender: "Male", ID: "A103" }, { Age: "21", Gender: "Male", ID: "A101" }, { Age: "22", Gender: "Female", ID: "A114" }, { Age: "23", Gender: "Female", ID: "A112" }, { Age: "22", Gender: "Female", ID: "A108" }, { Age: "21", Gender: "Female", ID: "A104" }, { Age: "23", Gender: "Female", ID: "A106" }, { Age: "22", Gender: "Female", ID: "A102" }, { Age: "21", Gender: "Female", ID: "A110" }],
keys = ['Gender', 'Age'],
tree = data
.reduce((r, o) => {
keys
.reduce((level, key) => {
let name;
({ [key]: name, ...o } = o);
if (!level[name]) {
level[name] = { _: [] };
level._.push({ name, children: level[name]._ });
}
return level[name];
}, r)
._
.push(o);
return r;
}, { _: [] })
._;
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

How do i create a function that will change the property inside an object that is located inside of an array, in a JSON

so ive been currently stuck on a JavaScript assignment for hours now.
so ive been given a Json file with the following data :
{
"owner": "Jun31d",
"info": [{ "id": 1, "name": "bob", "flavour": "vanilla", "age": 43 },
{ "id": 2, "name": "Juneid", "flavour": "Chocolate", "age": 19 },
{ "id": 3, "name": "Patrick", "flavour": "Strawberry", "age": 25 },
{ "id": 4, "name": "Jean", "flavour": "Mint", "age": 31 },
{ "id": 5, "name": "Ahmad", "flavour": "vanilla", "age": 18 },
{ "id": 6, "name": "Ali", "flavour": "Bubblegum", "age": 19 },
{ "id": 7, "name": "Frank", "flavour": "Pistachio", "age": 23 }
]
}
The instruction for my assigment is the following :
Instruction
So from what ive understood i need to create a function that holds two string parameters, And ill need to change the property inside of my object that is located in an array to a new property.
And until now heres what i did :
'use strict'
const iceCream = require('./main.json')
let namespace = {
changeProp : function (newprop,oldprop) {
for (let oldprop in iceCream.info) {
}
}
}
I know it is not much at all, but i just want some help to know how can i move forward a little bit more on my assigment.
Any help is appreciated, thank you
You can easily replace the oldKey with newKey using map, Object.keys, and reduce
const data = {
owner: "Jun31d",
info: [
{ id: 1, name: "bob", flavour: "vanilla", age: 43 },
{ id: 2, name: "Juneid", flavour: "Chocolate", age: 19 },
{ id: 3, name: "Patrick", flavour: "Strawberry", age: 25 },
{ id: 4, name: "Jean", flavour: "Mint", age: 31 },
{ id: 5, name: "Ahmad", flavour: "vanilla", age: 18 },
{ id: 6, name: "Ali", flavour: "Bubblegum", age: 19 },
{ id: 7, name: "Frank", flavour: "Pistachio", age: 23 },
],
};
function changePropertyName(arr, oldName, newName) {
return arr.map((o) => {
return Object.keys(o).reduce((acc, curr) => {
curr === oldName ? (acc[newName] = o[oldName]) : (acc[curr] = o[curr]);
return acc;
}, {});
});
}
console.log(changePropertyName(data.info, "flavour", "bestFlavour"));
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is how you can do it. Easy, simple and nasty solution.
const data = {
owner: 'Jun31d',
info: [
{ id: 1, name: 'bob', flavour: 'vanilla', age: 43 },
{ id: 2, name: 'Juneid', flavour: 'Chocolate', age: 19 },
{ id: 3, name: 'Patrick', flavour: 'Strawberry', age: 25 },
{ id: 4, name: 'Jean', flavour: 'Mint', age: 31 },
{ id: 5, name: 'Ahmad', flavour: 'vanilla', age: 18 },
{ id: 6, name: 'Ali', flavour: 'Bubblegum', age: 19 },
{ id: 7, name: 'Frank', flavour: 'Pistachio', age: 23 }
]
};
const renameProperty = (a, e, n) =>
a.map((el) => {
el[n] = el[e];
delete el[e];
return el;
});
console.log(renameProperty(data.info, "id", "_id"));

Compare two arrays of objects, where objects in each array have different properties

I have two result sets like this:
const resultSet1 =
[
{
"id": "1",
"version": "3",
"website": "https://xx/version/3",
"name": Ana,
"lastName": Ana,
},
{
"id": "2",
"version": "3",
"website": "https://xx/version/3",
"name": Ana,
"lastName": Ana,
}
]
const resultSet2 =
[
{
"id": "1",
"version": "2",
"birthday": "24.08.1984",
"place": "Europe",
},
{
"id": "2",
"version": "2",
"birthday": "24.08.1984",
"place": "Europe",
},
{
"id": "1",
"version": "1",
"birthday": "24.08.1984",
"place": "Europe",
},
{
"id": "2",
"version": "3",
"birthday": "24.08.1984",
"place": "Europe",
}
]
I want to compare these two result sets, based on id & version. In my const comparisonSet, I want to have elements from the first result set, whose both id & version are not present in the second result set.
const comparisonSet =
[
{
"id": "1",
"version": "3",
"website": "https://xx/version/3",
"name": Ana,
"lastName": Ana,
}
]
How can I achieve this in Javascript?
Any help would be appreciated. Thank you in advance!
You can use filter to get the desired result.
Overall complexity - O(n * 2)
resultSet1.filter(({ id, version }) =>!resultSet2.find((o) => o.id === id && o.version === version));
const resultSet1 = [{
id: "1",
version: "3",
website: "https://xx/version/3",
name: "Ana",
lastName: "Ana",
},
{
id: "2",
version: "3",
website: "https://xx/version/3",
name: "Ana",
lastName: "Ana",
},
];
const resultSet2 = [{
id: "1",
version: "2",
birthday: "24.08.1984",
place: "Europe",
},
{
id: "2",
version: "2",
birthday: "24.08.1984",
place: "Europe",
},
{
id: "1",
version: "1",
birthday: "24.08.1984",
place: "Europe",
},
{
id: "2",
version: "3",
birthday: "24.08.1984",
place: "Europe",
},
];
const result = resultSet1.filter(
({
id,
version
}) =>
!resultSet2.find((o) => o.id === id && o.version === version)
);
console.log(result);
Though it is not so optimized, so you can also create a dictionary and loop up result in O(1) -
Overall complexity O(n)
const dict = resultSet2.reduce((acc, curr) => {
const { id, version } = curr;
acc[`${id}|${version}`] = curr;
return acc;
}, {});
const result = resultSet1.filter(({ id, version }) => !dict[`${id}|${version}`]);
const resultSet1 = [
{
id: "1",
version: "3",
website: "https://xx/version/3",
name: "Ana",
lastName: "Ana",
},
{
id: "2",
version: "3",
website: "https://xx/version/3",
name: "Ana",
lastName: "Ana",
},
];
const resultSet2 = [
{
id: "1",
version: "2",
birthday: "24.08.1984",
place: "Europe",
},
{
id: "2",
version: "2",
birthday: "24.08.1984",
place: "Europe",
},
{
id: "1",
version: "1",
birthday: "24.08.1984",
place: "Europe",
},
{
id: "2",
version: "3",
birthday: "24.08.1984",
place: "Europe",
},
];
const dict = resultSet2.reduce((acc, curr) => {
const { id, version } = curr;
acc[`${id}|${version}`] = curr;
return acc;
}, {});
const result = resultSet1.filter(({ id, version }) => !dict[`${id}|${version}`]);
console.log(result);
I would iterate through one array while filtering the other.
resultSet1.forEach(res1 => {
const filtered = resultSet2.filter(res2 => res2.id === res1.id && res2.version === res1.version);
//if filtered.length === 0 you can do what you want with res1
});
const Ana = 'Ana';
const resultSet1 = [
{ id: '1', version: '3', website: 'https://xx/version/3', name: Ana, lastName: Ana, },
{ id: '2', version: '3', website: 'https://xx/version/3', name: Ana, lastName: Ana, },
];
const resultSet2 = [
{ id: '1', version: '2', birthday: '24.08.1984', place: 'Europe', },
{ id: '2', version: '2', birthday: '24.08.1984', place: 'Europe', },
{ id: '1', version: '1', birthday: '24.08.1984', place: 'Europe', },
{ id: '2', version: '3', birthday: '24.08.1984', place: 'Europe', },
];
const idAndVersionNotInSecondResultSet = ({ id, version }) =>
resultSet2.every(({ id: i, version: v }) => i !== id || version !== v);
const comparisonSet = resultSet1.filter(idAndVersionNotInSecondResultSet);
console.log(comparisonSet);

Accessing an object property from outside scope

So I have 4 jsons with that looks like this:
{
"group": "A",
"id": "50"
"person": [
{
"name": 'Joe',
"age": '29'
},
{
"name": 'Jessie',
"age": '27'
}
]
}
I used this function to create an array with all the people from 4 different json's files.
list.forEach(list => {
list.person.forEach(person => {
peopleArray.push(person);
});
})
The problem is, when I pick a position from that array, I want to be able to access the group and the ID as well for example:
console.log(peopleArray[1].group);
Is that possible? Or I would have to those values inside the person?
Just include those values in the person object
const data = {
group: "A",
id: "50",
person: [
{
name: 'Joe',
age: '29'
},
{
name: 'Jessie',
age: '27'
}
]
}
data.person.map(obj => ({...obj, group: data.group, groupId: data.id}))
The result is:
[
{
age: "29",
group: "A",
groupId: "50",
name: "Joe"
},
{
age: "27",
group: "A",
groupId: "50",
name: "Jessie"
}
]

Group data by first char in JSON response

I want to group JSON by First char of first name but the resultant object should have two attributes "Key" & "Data"
I tried using lodash which has given me partial expected result but not the complete.
Following are the details with data, expected result, current result and current code implemented using lodash.
Any help will be appreciated.
JSON:
UserData = [
{
"gender":"male",
"name":{
"title":"mr",
"first":"landon",
"last":"gonzalez",
},
"location":{
"street":"7927 bollinger rd",
"city":"madison",
"state":"washington",
"postcode":24642
},
"email":"landon.gonzalez#example.com",
"dob":"1972-04-26 11:40:09",
"registered":"2013-07-04 17:42:44",
"phone":"(038)-931-4026",
"cell":"(808)-824-5320",
"nat":"US"
},
{
"gender":"male",
"title":"mr",
"first_name":"jonathan",
"last_name":"petersen",
"location":{
"street":"2583 brorsonsvej",
"city":"brøndby strand",
"state":"hovedstaden",
"postcode":87814
},
"email":"jonathan.petersen#example.com",
"dob":"1948-05-06 21:48:27",
"registered":"2009-03-09 17:04:40",
"phone":"03441612",
"cell":"73824887",
"nat":"DK"
},
{
"gender":"male",
"name":{
"title":"mr",
"first":"roméo",
"last":"marchand",
},
"location":{
"street":"9471 rue bony",
"city":"créteil",
"state":"maine-et-loire",
"postcode":30698
},
"email":"roméo.marchand#example.com",
"dob":"1969-08-18 16:41:01",
"registered":"2015-04-21 19:26:04",
"phone":"04-43-18-74-25",
"cell":"06-83-89-77-72",
"nat":"FR"
}
]
Expected:
[
{ key: 'A', data: [{...}, {...}, {...}] },
{ key: 'B', data: [{...}, {...}, {...}] },
{ key: 'C', data: [{...}, {...}, {...}] },
]
Current:
[
{ 'A': [{...}, {...}, {...}] },
{ 'B': [{...}, {...}, {...}] },
{ 'C': [{...}, {...}, {...}] },
]
Current Code:
sectionListData(users){
let sectionedUsers = _.groupBy(users, function(user) {
return user.first_name.charAt(0).toUpperCase();
});
alert(JSON.stringify(sectionedUsers));
}
Use lodash.chain and after grouping, map the results to create objects of your choice:
let UserData = [{
"gender": "male",
"name": {
"title": "mr",
"first": "landon",
"last": "gonzalez",
},
"location": {
"street": "7927 bollinger rd",
"city": "madison",
"state": "washington",
"postcode": 24642
},
"email": "landon.gonzalez#example.com",
"dob": "1972-04-26 11:40:09",
"registered": "2013-07-04 17:42:44",
"phone": "(038)-931-4026",
"cell": "(808)-824-5320",
"nat": "US"
},
{
"gender": "male",
"title": "mr",
"first_name": "jonathan",
"last_name": "petersen",
"location": {
"street": "2583 brorsonsvej",
"city": "brøndby strand",
"state": "hovedstaden",
"postcode": 87814
},
"email": "jonathan.petersen#example.com",
"dob": "1948-05-06 21:48:27",
"registered": "2009-03-09 17:04:40",
"phone": "03441612",
"cell": "73824887",
"nat": "DK"
},
{
"gender": "male",
"name": {
"title": "mr",
"first": "roméo",
"last": "marchand",
},
"location": {
"street": "9471 rue bony",
"city": "créteil",
"state": "maine-et-loire",
"postcode": 30698
},
"email": "roméo.marchand#example.com",
"dob": "1969-08-18 16:41:01",
"registered": "2015-04-21 19:26:04",
"phone": "04-43-18-74-25",
"cell": "06-83-89-77-72",
"nat": "FR"
}
];
let sectionedUsers = _.chain(UserData)
.groupBy(function(user) {
let firstName = _.get(user, 'name.first') || user.first_name;
return firstName.charAt(0).toUpperCase();
})
.map((data, key) => ({
key,
data
}))
.value();
console.log(sectionedUsers);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
You could _.map the grouped result.
var userData = [{ gender: "male", name: { title: "mr", first: "landon", last: "gonzalez" }, location: { street: "7927 bollinger rd", city: "madison", state: "washington", postcode: 24642 }, email: "landon.gonzalez#example.com", dob: "1972-04-26 11:40:09", registered: "2013-07-04 17:42:44", phone: "(038)-931-4026", cell: "(808)-824-5320", nat: "US" }, { gender: "male", title: "mr", name: { first: "jonathan", last: "petersen" }, location: { street: "2583 brorsonsvej", city: "brøndby strand", state: "hovedstaden", postcode: 87814 }, email: "jonathan.petersen#example.com", dob: "1948-05-06 21:48:27", registered: "2009-03-09 17:04:40", phone: "03441612", cell: "73824887", nat: "DK" }, { gender: "male", name: { title: "mr", first: "roméo", last: "marchand" }, location: { street: "9471 rue bony", city: "créteil", state: "maine-et-loire", postcode: 30698 }, email: "roméo.marchand#example.com", dob: "1969-08-18 16:41:01", registered: "2015-04-21 19:26:04", phone: "04-43-18-74-25", cell: "06-83-89-77-72", nat: "FR" }];
let sectionedUsers = _(userData)
.groupBy(user => user.name.first[0].toUpperCase())
.map((data, key) => ({ key, data }))
.value();
console.log(sectionedUsers);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>

linq.js Group By then Group By, How can it be achieved?

I want something that would result in this
[{
firstName: "Sam",
lastName: "Smith"
}, {
firstName: "Sam",
lastName: "Doe"
}, {
firstName: "Sam",
lastName: "Doe"
}, {
firstName: "Sam",
lastName: "Joe"
}]
Being something like this, with the exception of the number of columns to group with which is not fixed and the order of the columns as well
[{
"Sam": [{
"Smith": [{
firstName: "Sam",
lastName: "Smith"
}]
}, {
"Doe": [{
firstName: "Sam",
lastName: "Doe"
}, {
firstName: "Sam",
lastName: "Doe"
}]
}, {
"Joe": [{
firstName: "Sam",
lastName: "Joe"
}]
}]
}]
I want to be able to build something like the following photo demonstrates
I tried several times but i can't get a grasp on it, please help me :).
I propose a different output structure. Your structure seems convenient at first glance, but in further processing it will prove unnecessarily difficult to handle.
I would suggest this generic structure:
[
{
key: "group 1",
items: [{
key: "group 1.1",
items: [ {}, {}, {} ]
}, {
key: "group 1.2",
items: [ {}, {} ]
}]
}, {
key: "group 2",
items: [{
key: "group 2.1",
items: [ {}, {}, [}, {} ]
}, {
key: "group 2.2",
items: [ {} ]
}]
}
]
You can create a structure like this relatively easily:
var grouped = Enumerable.From(input).GroupBy("$.firstName", "", function(key, e) {
return {
key: key,
items: e.GroupBy("$.lastName", "", function (key, e) {
return {
key: key,
items: e.ToArray()
};
}).ToArray()
};
}).ToArray();
when applied to this input:
var input = [{
firstName: "Sam",
lastName: "Smith"
}, {
firstName: "Sam",
lastName: "Doe"
}, {
firstName: "Sam",
lastName: "Doe"
}, {
firstName: "Sam",
lastName: "Joe"
},{
firstName: "John",
lastName: "Joe"
}];
results in
[
{
"key": "Sam",
"items": [
{
"key": "Smith",
"items": [
{
"firstName": "Sam",
"lastName": "Smith"
}
]
},
{
"key": "Doe",
"items": [
{
"firstName": "Sam",
"lastName": "Doe"
},
{
"firstName": "Sam",
"lastName": "Doe"
}
]
},
{
"key": "Joe",
"items": [
{
"firstName": "Sam",
"lastName": "Joe"
}
]
}
]
},
{
"key": "John",
"items": [
{
"key": "Joe",
"items": [
{
"firstName": "John",
"lastName": "Joe"
}
]
}
]
}
]
Edit: To allow dynamically configurable grouping and ordering, a more advanced approach must be taken:
// Enumerable, definition => Enumerable
function applyOrder(items, definition) {
var i, prop, prefix, suffix, orderFunc;
if (!items) return;
if (!definition) return items;
for (i = 0; i < definition.length; i++) {
// definition[i] is either "propertyName" or "propertyName DESC"
prop = (definition[i] + " ").split(" ");
prefix = i === 0 ? "OrderBy" : "ThenBy";
suffix = prop[1].toUpperCase() === "DESC" ? "Descending" : "";
orderFunc = prefix + suffix;
items = items[orderFunc]("$." + prop[0]);
}
return items;
}
// Enumerable, definition => Enumerable
function applyGroup(items, definition) {
if (!items) return;
if (!definition) return items;
items = applyOrder(items, definition.order);
if (!definition.group) return items;
return items.GroupBy("$." + definition.group, "", function (key, e) {
return {
group: definition.group,
key: key,
items: applyGroup(e, definition.then).ToArray()
};
});
}
// Array, definition => Array
function applyStructure(items, definition) {
if (!items) return;
if (!definition) return items;
return applyGroup(Enumerable.From(items), definition).ToArray();
}
used like this:
var result = applyStructure(companies, {
group: "country",
order: ["country"],
then: {
group: "city",
order: ["city"],
then: {
order: ["companyName DESC"]
}
}
});
live at: http://jsfiddle.net/Tomalak/xth6ayuo/

Categories

Resources