Reputation: 185
I have following data structure:
var machines = [
{"endUser": "Multi Corp", "Location": "Germany", "MachineName": "Machine1"},
{"endUser": "Multi Corp", "Location": "Germany", "MachineName": "Teeth"},
{"endUser": "Multi Corp", "Location": "Germany", "MachineName": "Aaaabcc"},
{"endUser": "Multi Corp", "Location": "France", "MachineName": "FAST"},
{"endUser": "Multi Corp", "Location": "France", "MachineName": "Slow"},
{"endUser": "Test", "Location": "Germany", "MachineName": "Swess"},
{"endUser": "Test", "Location": "Germany", "MachineName": "aswees"},
{"endUser": "Test", "Location": "France", "MachineName": "Abcd"},
{"endUser": "Test", "Location": "France", "MachineName": "Efghhh"}
];
Now I what I need to do in order:
1. Group data by endUser in asc order
2. For each grouped row group inside data by Location in asc order
3. Sort by MachineName in asc order
Output should therefore look like:
I wrote code that does that but I am just beginning to work with Lodash and I am sure that my code can be refactored to much easier form because right now even I want to kill myself because of it but I do not known how to improve it due to lack of lodash experience.
Code:
var machines = [
{"endUser": "Multi Corp", "Location": "Germany", "MachineName": "Machine1"},
{"endUser": "Multi Corp", "Location": "Germany", "MachineName": "Teeth"},
{"endUser": "Multi Corp", "Location": "Germany", "MachineName": "Aaaabcc"},
{"endUser": "Multi Corp", "Location": "France", "MachineName": "FAST"},
{"endUser": "Multi Corp", "Location": "France", "MachineName": "Slow"},
{"endUser": "Test", "Location": "Germany", "MachineName": "Swess"},
{"endUser": "Test", "Location": "Germany", "MachineName": "aswees"},
{"endUser": "Test", "Location": "France", "MachineName": "Abcd"},
{"endUser": "Test", "Location": "France", "MachineName": "Efghhh"}
];
var machinesByEndUser = _.chain(machines)
.sortBy(function(machine) { return machine.endUser.toLocaleLowerCase() })
.groupBy(function(machine) { return machine.endUser})
.value();
var machinesFinal = {};
_.forOwn(machinesByEndUser, function(value, key){
var machinesByLocationPerUser = _.chain(value)
.sortBy(function(machine) { return machine.Location.toLocaleLowerCase() })
.groupBy(function(machine) { return machine.Location})
.value();
var orderMachines = [];
_.forOwn(machinesByLocationPerUser, function(value, key){
var sortedMachines = _.sortBy(value, function(machine) {return machine.MachineName.toLocaleLowerCase()});
orderMachines.push(sortedMachines);
});
var flatten = _.flatten(orderMachines);
var orderedMachinesByLocation = _.groupBy(flatten, function(singleMach) {return singleMach.Location});
machinesByEndUser[key] = orderedMachinesByLocation;
} );
console.log(machinesByEndUser);
Any help would be much appreciated.
Upvotes: 1
Views: 323
Reputation: 30088
You can do the following:
Use orderBy() to order the collection by endUser
and Location
in ascending order.
Use groupBy() to group the collection by endUser
.
mapValues() and groupBy() to group each grouped endUser
by Location
.
var result = _(machines)
.orderBy(['endUser', 'Location'])
.groupBy('endUser')
.mapValues(_.partial(_.groupBy, _, 'Location'))
.value();
var machines = [{
"endUser": "Multi Corp",
"Location": "Germany",
"MachineName": "Machine1"
}, {
"endUser": "Multi Corp",
"Location": "Germany",
"MachineName": "Teeth"
}, {
"endUser": "Multi Corp",
"Location": "Germany",
"MachineName": "Aaaabcc"
}, {
"endUser": "Multi Corp",
"Location": "France",
"MachineName": "FAST"
}, {
"endUser": "Multi Corp",
"Location": "France",
"MachineName": "Slow"
}, {
"endUser": "Test",
"Location": "Germany",
"MachineName": "Swess"
}, {
"endUser": "Test",
"Location": "Germany",
"MachineName": "aswees"
}, {
"endUser": "Test",
"Location": "France",
"MachineName": "Abcd"
}, {
"endUser": "Test",
"Location": "France",
"MachineName": "Efghhh"
}];
var result = _(machines)
.orderBy(['endUser', 'Location'])
.groupBy('endUser')
.mapValues(_.partial(_.groupBy, _, 'Location'))
.value();
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
Here's the lodash fp version for the lodash solution above:
var result = compose(
mapValues(groupBy('Location')),
groupBy('endUser'),
orderBy(['endUser', 'Location'], ['asc', 'asc'])
)(machines);
var machines = [{
"endUser": "Multi Corp",
"Location": "Germany",
"MachineName": "Machine1"
}, {
"endUser": "Multi Corp",
"Location": "Germany",
"MachineName": "Teeth"
}, {
"endUser": "Multi Corp",
"Location": "Germany",
"MachineName": "Aaaabcc"
}, {
"endUser": "Multi Corp",
"Location": "France",
"MachineName": "FAST"
}, {
"endUser": "Multi Corp",
"Location": "France",
"MachineName": "Slow"
}, {
"endUser": "Test",
"Location": "Germany",
"MachineName": "Swess"
}, {
"endUser": "Test",
"Location": "Germany",
"MachineName": "aswees"
}, {
"endUser": "Test",
"Location": "France",
"MachineName": "Abcd"
}, {
"endUser": "Test",
"Location": "France",
"MachineName": "Efghhh"
}];
var { orderBy, groupBy, mapValues, compose } = _;
var result = compose(
mapValues(groupBy('Location')),
groupBy('endUser'),
orderBy(['endUser', 'Location'], ['asc', 'asc'])
)(machines);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.fp.min.js"></script>
If you simply want a vanilla JS solution you can do this:
var result = machines
.slice() // Use slice to make sure that we don't mutate the original array
.sort(function(a, b) { // order by endUser and Location in ascending order
return a.endUser.localeCompare(b.endUser) +
a.Location.localeCompare(b.Location);
})
.reduce(function(group, item) {
// group collection by endUser
var endUserGroup = group[item.endUser] = group[item.endUser] || {};
// group endUser group by Location
var locationGroup = endUserGroup[item.Location] = endUserGroup[item.Location] || [];
locationGroup.push(item);
return group;
}, {});
var machines = [{
"endUser": "Multi Corp",
"Location": "Germany",
"MachineName": "Machine1"
}, {
"endUser": "Multi Corp",
"Location": "Germany",
"MachineName": "Teeth"
}, {
"endUser": "Multi Corp",
"Location": "Germany",
"MachineName": "Aaaabcc"
}, {
"endUser": "Multi Corp",
"Location": "France",
"MachineName": "FAST"
}, {
"endUser": "Multi Corp",
"Location": "France",
"MachineName": "Slow"
}, {
"endUser": "Test",
"Location": "Germany",
"MachineName": "Swess"
}, {
"endUser": "Test",
"Location": "Germany",
"MachineName": "aswees"
}, {
"endUser": "Test",
"Location": "France",
"MachineName": "Abcd"
}, {
"endUser": "Test",
"Location": "France",
"MachineName": "Efghhh"
}];
var result = machines
.slice() // Use slice to make sure that we don't mutate the original array
.sort(function(a, b) { // order by endUser and Location in ascending order
return a.endUser.localeCompare(b.endUser) +
a.Location.localeCompare(b.Location);
})
.reduce(function(group, item) {
// group collection by endUser
var endUserGroup = group[item.endUser] = group[item.endUser] || {};
// group endUser group by Location
var locationGroup = endUserGroup[item.Location] = endUserGroup[item.Location] || [];
locationGroup.push(item);
return group;
}, {});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.fp.min.js"></script>
Upvotes: 1
Reputation: 50787
I tried this in the library I prefer, Ramda (disclaimer: I'm one of the authors.) And this is the code I wrote:
R.pipe(
R.groupBy(R.prop('endUser')),
R.map(R.groupBy(R.prop('Location'))),
R.map(R.map(R.sortBy(R.pipe(R.prop('MachineName'), R.toLower))))
)(machines);
This seems relatively clean. As far as I can tell, this should port over almost intact to lodash/fp, but I've never really had any luck getting that version to work properly.
Perhaps someone who knows lodash itself better can suggest a reasonable port into the main library. I got this far:
_.flow(
machines => _.groupBy(machines, machine => machine.endUser),
machines => _.map(machines, machines => _.groupBy(machines, machine => machine.Location))
)(machines)
and got disheartened. I also tried playing with flip
, but that was even worse. I know that the community has come down against chaining in lodash. (JDD has pointed to that article several times that I've seen.) But I'm struggling to find a relatively clean way to express these fairly simple concepts in lodash without chaining.
Update:
I did figure out how to run lodash/fp in a REPL. But the straightforward port did not work quite as hoped as groupWith
has a slightly different behavior.
Upvotes: -1
Reputation: 78850
Try this out:
var result = _(machines)
.groupBy(machine => machine.endUser.toLowerCase())
.mapValues(machines => (
_(machines)
.groupBy(machine => machine.Location.toLowerCase())
.mapValues(machines => (
_(machines)
.map('MachineName')
.sort()
.value()
))
.value()
))
.value()
Fiddle: https://jsfiddle.net/c38thoqn/
As others have correctly stated, objects can't really be sorted, by definition; ordering of an object is undefined. Certain JavaScript implementations happen to have object ordering, but it's not standard.
Upvotes: 2
Reputation: 122037
Here is pure Javascript approach with two forEach
loops
var machines = [{"endUser":"Multi Corp","Location":"Germany","MachineName":"Machine1"},{"endUser":"Multi Corp","Location":"Germany","MachineName":"Teeth"},{"endUser":"Multi Corp","Location":"Germany","MachineName":"Aaaabcc"},{"endUser":"Multi Corp","Location":"France","MachineName":"FAST"},{"endUser":"Multi Corp","Location":"France","MachineName":"Slow"},{"endUser":"Test","Location":"Germany","MachineName":"Swess"},{"endUser":"Test","Location":"Germany","MachineName":"aswees"},{"endUser":"Test","Location":"France","MachineName":"Abcd"},{"endUser":"Test","Location":"France","MachineName":"Efghhh"}];
var result = {};
machines.forEach(function(e) {
result[e.endUser] = {}
})
machines.forEach(function(e) {
result[e.endUser][e.Location] = (result[e.endUser][e.Location] || []).concat(e).sort(function(a, b) {
return a.MachineName.localeCompare(b.MachineName);
});
});
document.body.innerHTML = '<pre>' + JSON.stringify(result, 0, 4) + '</pre>';
Upvotes: 0