Home > Software engineering >  Sorting is not correct when using parse date-fns
Sorting is not correct when using parse date-fns

Time:11-15

I have 4 dates with the following data but when I am sorting them the data is coming out to be wrong.

Dates:


2h, 2m, 13s
5d, 3h, 49m, 42s
2h, 0m, 13s
12h, 32m, 13s

I am trying to use this formula to convert them to date using Date-fns parse

     convertStringToDate(value) {
        if (value.match('M')) {
            return dateFns.parse(value, "M'M', dd'd', HH'h', mm'm', ss's'", new Date())
        }
        else if (value.match('d')) {
             return dateFns.parse(value, "dd'd', HH'h', mm'm', ss's'", new Date())
        }
        else if (value.match('h')) {
             return dateFns.parse(value, "HH'h', mm'm', ss's'", new Date())
        }
        else if (value.match('m')) {
             return dateFns.parse(value, "mm'm', ss's'", new Date())
        }
        else if (value.match('s')) {
             return dateFns.parse(value, "ss's'", new Date())
        }
    }

and then sorting it

$.fn.dataTableExt.oSort["customDuration-desc"] = (a, b) => {
                        a = this.convertStringToDate(a);
                        b = this.convertStringToDate(b);
                        return new Date(a) - new Date(b);
                    };

The result is coming wrong. its displaying as

5d, 3h, 49m, 42s
2h, 2m, 13s
2h, 0m, 13s
12h, 32m, 13s

instead of

5d, 3h, 49m, 42s
12h, 32m, 13s
2h, 2m, 13s
2h, 0m, 13s

CodePudding user response:

You can simply parse your duration strings and convert each unit to seconds, here using an object indexed by your unit abbreviations to hold conversion methods. (I've removed Months as there is no standard month length in seconds)

function durationToSeconds(value) {
  const unitMap = {
    d: (d) =>  d * 24 * 60 * 60,
    h: (h) =>  h * 60 * 60,
    m: (m) =>  m * 60,
    s: (s) =>  s,
  }

  return value
    .split(',')
    .reduce((a, u) => {
      const { v, k } = u.match(/(?<v>\d )(?<k>\w)/).groups
      return a   unitMap[k]?.(v) ?? 0;
    }, 0);
}

const input = [
  "2h, 2m, 13s",
  "5d, 3h, 49m, 42s",
  "2h, 0m, 13s",
  "12h, 32m, 13s"
];

const result = input.sort((a, b) => {
  a = durationToSeconds(a);
  b = durationToSeconds(b);

  return b - a;
});

console.log(result);

Alternatively you could do something like the following which creates a duration object from your string values and then uses date-fns.add() to add each duration to a pre-specified date (here held in a closure). Note: That introducing a Date into the calculation introduces a lot more complexity depending on how you deal with them, timezone/local vs utc/daylight savings.

<script type='module'>
import { add } from 'https://esm.run/date-fns';

function addDuration(date) {
  const unitMap = {
    d: 'days',
    h: 'hours',
    m: 'minutes',
    s: 'seconds',
  }

  return (value) => {
    const duration = Object.assign(
      {
        years: 0,
        months: 0,
        weeks: 0,
        days: 0,
        hours: 0,
        minutes: 0,
        seconds: 0
      },
      Object.fromEntries(value.split(',').map(u => {
        const { v, k } = u.match(/(?<v>\d )(?<k>\w)/).groups
        return [unitMap[k], v];
      }))
    );

    return add(date, duration)
  }
}

const addDurationToDate = addDuration(new Date());

const result = [
  "2h, 2m, 13s",
  "5d, 3h, 49m, 42s",
  "2h, 0m, 13s",
  "12h, 32m, 13s"
].sort((a, b) => {
  a = addDurationToDate(a);
  b = addDurationToDate(b);

  return b - a;
});

console.log(result);

</script>

  • Related