0

I have the following code:

    this.userActionsRights$ = combineLatest([this.sortBy$, this.userActionsRights$]).pipe(
        map(([sortBy, userRights]) => {
            const { active, direction } = sortBy;
            const sortByAsc = direction === 'asc';
            let sortFn;

            if (active === 'name') sortFn = compareString;
            if (active === 'id') sortFn = compareNumber;

            return userRights.sort(sortFn);
        }),
    );

Where compare string is:

export function compareString(a: string | undefined, b: string | undefined, isAsc: boolean) {
  if (a === undefined || a === null) return isAsc ? 1 : -1
  if (b === undefined || b === null) return isAsc ? -1 : 1

  return isAsc ? b.localeCompare(a) : a.localeCompare(b)
}

How to pass sortByAsc to compareString and then apply it below in userRights.sort(sortFn);.

  • I got the problem, I have an array of objects, so I need to sort by object fields name or id –  Nov 03 '21 at 16:54

2 Answers2

1

An easy way to solve your problem would be to wrap sortFn in a function that inverts the result if the sort direction is "desc".

const userRights = /* an array */;
const sortFn = /* sort callback function that sorts asc */;
const direction = /* the sort direction */;
const modifiers = { asc: 1, desc: -1 };

// Forward the `a` and `b` arguments to `sortFn` and multiply the return
// value with the modifier.
return userRights.sort((a, b) => sortFn(a, b) * modifiers[direction]);

Note that direction must be either "asc" or "desc", otherwise NaN is returned. (A number multiplied by undefined results in NaN.)

To allow both uppercase and lowercase characters you might want to downcase the direction up front.

direction = direction.toLowerCase(); // <- you have to define direction with `let`

You probably also want to throw an error if an invalid direction is passed.

if (!(direction in modifiers)) {
  throw new Error(`Invalid sort direction "${direction}".`);
}
3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
0

You can just create a new arrow function on the fly, like so:

this.userActionsRights$ = combineLatest([this.sortBy$, this.userActionsRights$])
  .pipe(
    map(([sortBy, userRights]) => {
      const { active, direction } = sortBy;
      const sortByAsc = direction === 'asc';
      let sortFn;

      // Here instead of passing the method directly,
      // we create a new one with the correct parameters passed
      if (active === 'name') sortFn = (a, b) => compareString(a, b, sortByAsc);
      if (active === 'id') sortFn = compareNumber;

      return userRights.sort(sortFn);
    }),
  );
MauriceNino
  • 6,214
  • 1
  • 23
  • 60
  • `ERROR TypeError: a.localeCompare is not a function`. I get this error on the line: `compareString(a, b, sortByAsc);` –  Nov 03 '21 at 16:27
  • true, however `.sort().reverse()` is more efficient, see [this answer](https://stackoverflow.com/a/52030227/3393379) – thinkgruen Nov 03 '21 at 16:27
  • reverse is not working for numbers –  Nov 03 '21 at 16:29
  • 1
    @Jessy seems like that is a problem stemming from your data. Did you maybe `JSON.parse()` and `JSON.stringify()` class instances? – MauriceNino Nov 03 '21 at 16:30
  • I get always filled array `userRights`. I have checked –  Nov 03 '21 at 16:40
  • If you want any help, show us your problem. Nobody can help you if you don't show us the code and values @Jessy – MauriceNino Nov 04 '21 at 07:31