Source: sort.js

import { isgeojson, isarrayofobjects } from "./helpers/helpers";
import { descending, ascending } from "d3-array";
import { autoType } from "d3-dsv";
const d3 = Object.assign({}, { descending, ascending, autoType });

/**
 * @function sort
 * @summary Sorting data according to a field
 * @param {object|array} data - A GeoJSON FeatureCollection or an array of objects
 * @param {object} options - Optional parameters
 * @param {array} [options.key ] - Field on which sorting is performed
 * @param {array} [options.ascending = false] - To change the sort order
 * @param {boolean} [options.mutate = false] - Use `true` to update the input data. With false, you create a new object, but the input object remains the same.
 * @returns {object|array} -  A GeoJSON FeatureCollection or an array of objects. (it depends on what you've set as `data`).
 * @example
 * geotoolbox.sort(*a geojson or an array of objects*, {key: "ISO3", ascending: true})
 */

export function sort(data, { key, ascending = true, mutate = false } = {}) {
  let x = data;
  const test = ["", null, undefined, +Infinity, -Infinity, NaN];

  if (isgeojson(x)) {
    if (!mutate) {
      x = JSON.parse(JSON.stringify(data));
    }

    if (key != undefined) {
      let featurestosort = x.features.filter(
        (d) => !test.includes(d?.properties[key])
      );
      let rest = x.features.filter((d) => test.includes(d?.properties[key]));

      const mysort = ascending ? d3.ascending : d3.descending;
      featurestosort = featurestosort.sort((a, b) =>
        mysort(
          d3.autoType([String(a.properties[key])])[0],
          d3.autoType([String(b.properties[key])])[0]
        )
      );
      x.features = [...featurestosort, ...rest];
    }
  } else if (isarrayofobjects(x)) {
    if (key != undefined) {
      let xtosort = x.filter((d) => !test.includes(d[key]));
      let rest = x.filter((d) => test.includes(d[key]));
      const mysort = ascending ? d3.ascending : d3.descending;
      xtosort = xtosort.sort((a, b) =>
        mysort(
          d3.autoType([String(a[key])])[0],
          d3.autoType([String(b[key])])[0]
        )
      );
      x = [...xtosort, ...rest];
    }

    if (mutate) {
      data.splice(0, data.length, ...x);
    }
  }

  return x;
}