0

I have an array with classes (the class is called Note), and in those classes are basic values. Two important values are the Note.Pinned (boolean whether it's pinned or not) and Note.Modified (a timestamp when it was last edited).

I would like to sort this array of classes by the Modified timestamp, then reorder it that the Pinned notes are on top of this list.

I already tried sorting the notes.

const SortedNotes = Notes.sort((a, b) => a.Modified > b.Modified);

Notes being the array with classes.

But, then, is there a better way to rearrange this array? Or is this the only method I can use?

SortedNotes.filter(n => n.Pinned).concat(SortedNotes.filter(n => !n.Pinned));

The above would work, and I know I can use Array.prototype.partition, so is there any other way of doing this?

Thanks

14
  • Please note: sort sorts in-place and modifies the existing array. Commented Mar 4, 2020 at 15:58
  • 3
    Why not just sort again for notes having the Pinned key be sent to the top? Commented Mar 4, 2020 at 15:58
  • 1
    If the Modified is actually a Date type, and Pinned is a boolean, this should work -> sort((a, b) => Number(b.Pinned) - Number(a.Pinned) || a.getTime() - b.getTime()) In sorting terminology it's known as a compound index sort. Commented Mar 4, 2020 at 16:05
  • 1
    (a, b) => a.Modified > b.Modified is an inherently unstable return value for sorting. Commented Mar 4, 2020 at 16:05
  • 1
    @Keith That is an interesting statement, Number(b.Pinned). I will try out how that all works and thanks for the answer! Commented Mar 4, 2020 at 16:18

5 Answers 5

2

Not the best solution perhaps, but this should help you in the right direction. This sorts the array so that notes are in decending date order, with the pinned ones at the top.

Unlike other (better) answers, this has a O(2n) sorting complexity, so bear that in mind.

const notes = [
  {
    text: "foo",
    modified: 1583337610387,
    pinned: false
  },
  {
    text: "bar",
    modified: 1583337610388,
    pinned: true
  },
  {
    text: "baz",
    modified: 1583337610389,
    pinned: false
  },
  {
    text: "qux",
    modified: 1583337610390,
    pinned: true
  }
];

const sortedNotes = [...notes];
sortedNotes.sort((a, b) => a.modified > b.modified ? -1 : 1);
sortedNotes.sort((a, b) => (a.pinned === b.pinned) ? 0 : a.pinned ? -1 : 1);
console.log(sortedNotes);

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

Comments

2

You can just add the pinned sorting requirement to the sort:

const arr = [{
    pinned: false,
    modified: 7
  },
  {
    pinned: false,
    modified: 6
  },
  {
    pinned: true,
    modified: 2
  },
  {
    pinned: false,
    modified: 4
  },
  {
    pinned: true,
    modified: 1
  },
  {
    pinned: true,
    modified: 8
  },
  {
    pinned: false,
    modified: 3
  },
]

arr.sort((a, b) => {
  if (a.pinned != b.pinned)
    return a.pinned ? -1 : 1;
  return b.modified - a.modified;
})

console.log(arr)

As long as the execution of the sort function returns the same result for the same two given parameters the sort will work fine. Knowing this, you can handle the two states where the pinned state is different and then sort by modified only if the pinned state is the same.

2 Comments

I've edited it to remove the number of checks required each call. It's now: If the pinned state is different and the first item is pinned put it before the second item otherwise put it after, else sort by modified as normal.
If you'd like an even shorter version, try this -> arr.sort((a,b)=>(b.pinned|0)-(a.pinned|0)||b.modified-a.modified);
1

You can sort by multiple conditions:

const SortedNotes = Notes.sort((a, b) => {
  if (a.Pinned == b.Pinned) {
    if (a.Modified > b.Modified) return -1;
    if (a.Modified < b.Modified) return 1;
  } else {
    return a.Pinned ? -1 : 1;
  }
});

The missing branch of a.Pinned == b.Pinned && a.Modified == b.Modified would return undefined, which in turn would be interpreted as equal.

Btw, the array Notes itself would've been modified in place, so both SortedNotes and Notes would exhibit the same ordering; of course, assigning to a const variable can't hurt.

Comments

0

Shorter, you can try

const SortedNotes = Notes.sort((a, b) => {
    if (a.Pinned > b.Pinned)
       return 1;
    if (a.Pinned == b.Pinned) 
       return a.Modified > b.Modified;
    return -1;
})

1 Comment

This compare function would return the same for a.Modified == b.Modified as well as a.Modified < b.Modified ... this isn't guaranteed to be well supported.
0

I edited @Ja͢ck's answer to be a little more compact.

const SortedNotes = Notes.sort((a, b) => {
    if (a.Pinned == b.Pinned) return a.Modified > b.Modified ? -1 : 1;
    else return a.Pinned ? -1 : 1;
});

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.