blackhole://nilFM

recursive multicolumn sort in react

This comes with a little regex magic to allow sorting numbers with an optional unit appended. The state variable sortBy is an array of simple obects with keys key and order

useEffect(() => {
  const unitregex = /(\d+\.\d+|\d+)([^\d.]+)/;
  const sortFunctions = {
    number: (a, b) => {
      const unitA = isNaN(Number(a)) ? a.match(unitregex)?.[2] : null;
      const unitB = isNaN(Number(b)) ? b.match(unitregex)?.[2] : null;
      if (unitA && unitB) {
        return Number(a.split(unitA)[0]) > Number(b.split(unitB)[0])
          ? 1
          : Number(a.split(unitA)[0]) < Number(b.split(unitB)[0])
          ? -1
          : 0;
      } else {
        return Number(a) > Number(b) ? 1 : Number(a) < Number(b) ? -1 : 0;
      }
    },
    default: (a, b) => {
      return a > b ? 1 : a < b ? -1 : 0;
    },
  };

  const metaSort = (a, b) => {
    let depth = sortBy.length;
    // eslint-disable-next-line
    const innerSort = (a, b) => {
      const sortingBy = sortBy[sortBy.length - depth];
      let res = (sortFunctions[
        columns.filter((c) => c.key === sortingBy.key)[0].filter.type
      ] || sortFunctions['default'])(a[sortingBy.key], b[sortingBy.key]);
      if (sortingBy.order === "descending") {
        res = res > 0 ? -1 : res < 0 ? 1 : 0;
      }
      if (res === 0 && depth > 1) {
        depth = depth - 1;
        res = innerSort(a, b);
      }
      return res;
    };
    return innerSort(a,b);
  };
  
  setSortedRows(rows.slice(0).sort(metaSort));
}, [sortBy, columns, rows]);