how to perform sort on array of luxon objects
Asked Answered
C

3

8

I am trying to use js sort of luxon time objects. I am not sure if this is correct as sort expects -1,0,1

const results  = objectGroupedByYearMonth[year][month].results.sort(
          (a,b) => 
            DateTime.fromISO(b.created)
              .diff(DateTime.fromISO(a.created))
        )

This returns a dt object console.log("DIF: ", DateTime.fromISO("2020-11-03T17:01:22.205041Z") .diff(DateTime.fromISO("2020-11-03T17:15:23.998284Z")))

Cytaster answered 12/11, 2020 at 10:17 Comment(0)
F
12

You might want to convert the DateTime objects to numerical Unix timestamps:

function compareLuxonDates(a: DateTime, b: DateTime) {
  return a.toMillis() - b.toMillis()
}
arrayWithDateObjects.sort(compareLuxonDates)
Fimble answered 16/11, 2020 at 9:34 Comment(0)
I
9

According to their docs, Luxon's DateTime class implements #valueof, which returns the primitive value of an object– in this case it's the epoch milliseconds. That means you can directly use < or > to compare Luxon DateTime objects when sorting.

Here's a one-liner compareFn I use to sort Luxon DateTime arrays sometimes.

The quick answer

sortDateTimes = (a, b) => a < b ? -1 : a > b ? 1 : 0;

This approach works because equality is the catch-all condition (the 0 at the end). Although you can use < and > freely with DateTime objects, you can't just use === to check for equality. Luxon provides a .equals(...) function for that. Alternative, because they implement #valueOf, you can make the DateTime objects convert to their primitive values using +.

const { DateTime } = require('luxon');

a = DateTime.fromISO('2020-01-15');
b = DateTime.fromISO('2020-01-15');

a === b; // false
+a === +b; // true

The little compareFn one-liner above works fine on an array of DateTime objects, but since we often want to sort an array of objects that the DateTime embedded in a property (like created), we have to take a slightly different approach.

sortByCreated = (a, b) =>  
  a.created < b.created ? -1 : a.created > b.created ? 1 : 0;

The robust answer

For bonus points, make this a little more reusable with a function generator.

createPropertySorter = (propertyName) => (a, b) =>
  a[propertyName] < b[propertyName] ? -1 : a[propertyName] > b[propertyName] ? 1 : 0;

The generator would then be used when you need to sort instead of the sort function itself directly. From OP's example:

const results = objectGroupedByYearMonth[year][month].results.sort(
    createPropertySorter('created')
  );

// results is now sorted, e.g. results.map(JSON.stringify) is
// [
//  '{"name":"some Object","created":"2020-01-25T00:00:00.000-07:00"}',
//  '{"name":"some Object","created":"2020-01-31T00:00:00.000-07:00"}',
//  '{"name":"some Object","created":"2020-02-01T00:00:00.000-07:00"}',
//  '{"name":"some Object","created":"2020-02-01T00:00:00.000-07:00"}',
//  '{"name":"some Object","created":"2020-02-07T00:00:00.000-07:00"}'
// ]

Watch out for mutability

A quick aside on mutability: in the example above, objectGroupedByYearMonth[year][month] has a property called results. Because sort() sorts arrays in-place, objectGroupedByYearMonth[year][month].results is also sorted (not just the returned value). This may have been the OP's intention, or it may be inconsequential, but I think it's worth minding. A revised version that preserves the original sorting would sort a copy of the original array using the spread operator.

const results = [...objectGroupedByYearMonth[year][month].results].sort(
    createPropertySorter('created')
  );
// results is sorted, while objectGroupedByYearMonth[year][month].results
// is still in its original order

Reverse sorting

If you want to reverse the sort order (latest dates first), switch a and b in the sort function.

createPropertyReverseSorter = (propertyName) => (a, b) =>
  b[propertyName] < a[propertyName] ? -1 : b[propertyName] > a[propertyName] ? 1 : 0;
Icecold answered 15/1, 2021 at 16:43 Comment(1)
This is a good answer. I like how you build it up from the quick to a more robust function generator. I got inspired and have the pleasure of working in TypeScript, so I did a version of it where you don't loose type safety: const sortByProperty = <TItem, TProperty>(propertySelector: PropertySelector<TItem, TProperty>) => (a: TItem, b: TItem): number => propertySelector(a) < propertySelector(b) ? -1 : propertySelector(a) > propertySelector(b) ? 1 : 0; Usage [...results].sort((a, b) => sortByProperty<Item, DateTime>(c => c.created)(a, b))Prostatitis
B
3

You could take the string directly for comparison with String#localeCompare.

(a, b) => b.created.localeCompare(a.created)
Birdlime answered 12/11, 2020 at 10:22 Comment(2)
So if I wanted asc or desc I would just reverse and b and aCytaster
@Cytaster Yes, if you want to reverse, you just switch those 2Burma

© 2022 - 2024 — McMap. All rights reserved.