2

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.

3
  • you need a stable sort, the build in is not stable IIRC. Commented Mar 31, 2020 at 13:12
  • what you mean by I've tried sorting by multiple items,? this doesn't sound right to me. Commented Mar 31, 2020 at 13:13
  • do you have always three items in a group? please add the wanted result. Commented Mar 31, 2020 at 13:53

5 Answers 5

3

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

Sign up to request clarification or add additional context in comments.

Comments

2

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}

Comments

2

You could

  • collect all object of the same group and get the total for sorting,
  • sort by the groups total value,
  • get a flat array of all objects.

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; }

Comments

0

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
  }
]

Comments

0

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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.