Reputation: 2366
I have a dataset that looks like this:
[
{
"name": "Item1",
"section": "section1",
"total": 3,
}, {
"name": "Item1",
"section": "section2",
"total": 4,
}{
"name": "Item1",
"section": "section3",
"total": 7,
}, {
"name": "Item2",
"section": "section1",
"total": 1,
}, {
"name": "Item2",
"section": "section2",
"total": 2,
}, {
"name": "Item2",
"section": "section3",
"total": 3,
}
]
I need to sort the array by only the total value in the section 3 item, but maintain the order (section1, section2, then section 3) per name. So for this example Item2 should move all 3 of it's rows above Item1. I've tried sorting by multiple items, but that doesn't maintain the ordering that I need. Should I just get the smallest/biggest, grab the related items and put them into a new array and repeat or is there a more logical way to accomplish this?
I'm also using angular and primeng grid if there's something I can leverage in there.
Upvotes: 2
Views: 8407
Reputation: 386654
You could
total
value,const
data = [{ name: "Item1", section: "section1", total: 3 }, { name: "Item1", section: "section2", total: 4 }, { name: "Item1", section: "section3", total: 7 }, { name: "Item2", section: "section1", total: 1 }, { name: "Item2", section: "section2", total: 2 }, { name: "Item2", section: "section3", total: 3 }],
result = Object
.values(data.reduce((r, o) => {
r[o.name] = r[o.name] || { payload: [] };
r[o.name].payload.push(o);
if (o.section === 'section3') r[o.name].total = o.total;
return r;
}, {}))
.sort(({ total: a }, { total: b }) => a - b)
.flatMap(({ payload }) => payload);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 2
Reputation: 8135
Priority sort
const data = [{"name":"Item1","section":"section1","total":3},{"name":"Item1","section":"section2","total":4},{"name":"Item1","section":"section3","total":7},{"name":"Item2","section":"section1","total":1},{"name":"Item2","section":"section2","total":2},{"name":"Item2","section":"section3","total":3}];
console.log(
data.sort((a, b) => {
const diff = a.total - b.total;
if (diff) return diff;
return b.section.localeCompare(a.section);
})
);
.as-console-row {color: blue!important}
Upvotes: 2
Reputation: 16440
You first need to "Group" the data set by name
and then do the sorting by total
.
let items = [{
"name": "Item1",
"section": "section1",
"total": 3,
}, {
"name": "Item1",
"section": "section2",
"total": 4,
}, {
"name": "Item1",
"section": "section3",
"total": 7,
}, {
"name": "Item2",
"section": "section1",
"total": 1,
}, {
"name": "Item2",
"section": "section2",
"total": 2,
}, {
"name": "Item2",
"section": "section3",
"total": 3,
}];
let groups = {};
for (let item of items) {
if (!groups[item.name]) {
groups[item.name] = {
data: []
};
}
// Grouping by `name`
groups[item.name].data.push(item);
// Store the `total`
if (item.section == "section3") {
groups[item.name].key = item.total;
}
}
// sort the groups, this will maintain the order of sections (1,2 and 3) in each group
let sortedGroups = Object.values(groups).sort((a, b) => {
return a.key - b.key; // ascending
});
// then flatten the groups
let flatten = [].concat(...sortedGroups.map(x => x.data));
console.log(flatten);
Upvotes: 0
Reputation: 619
var array = [
{
"name": "Item1",
"section": "section1",
"total": 3,
}, {
"name": "Item1",
"section": "section2",
"total": 4,
},{
"name": "Item1",
"section": "section3",
"total": 7,
}, {
"name": "Item2",
"section": "section1",
"total": 1,
}, {
"name": "Item2",
"section": "section2",
"total": 2,
}, {
"name": "Item2",
"section": "section3",
"total": 3,
}
];
array = array.sort((o1, o2)=>{
if(o1.section === o2.section && o1.section === 'section3') {
return o1.total - o2.total;
} else {
return o1.section === 'section3' ? 1 : -1;
}
});
console.log(array);
Here's the output
[
{
"name": "Item1",
"section": "section1",
"total": 3
},
{
"name": "Item1",
"section": "section2",
"total": 4
},
{
"name": "Item2",
"section": "section1",
"total": 1
},
{
"name": "Item2",
"section": "section2",
"total": 2
},
{
"name": "Item2",
"section": "section3",
"total": 3
},
{
"name": "Item1",
"section": "section3",
"total": 7
}
]
Upvotes: 0
Reputation: 13515
I would create a map using the name
as the key and the total
as the value for items where section
equals section3
. You can then sort using the map.
This will sort all items by the value of the total
in section3
, and preserve the original sort order where the sort value matches.
const map = new Map<string, number>(this.data
.filter(x => x.section === 'section3')
.map(x => [ x.name, x.total ]));
this.sorted = this.data.slice()
.sort((a, b) => map.get(a.name) - map.get(b.name));
This does rely on the data being structured and ordered as your have specified in your question.
DEMO: https://stackblitz.com/edit/angular-fsswdq
Upvotes: 3