3

I need to sort a list of units that may or may not contain a number.

For example ["unit 1", "unit 2", ..., "unit 11"].

Most sorting function will order this: unit 1, unit 11, unit 2..

But I may also have the case that doesn't have a number. ["apt A", "apt B", ..., "apt Z"].

Are there any clever solutions that would sort this correctly:

unit 1, unit 2, ..., unit 11.

apt A, apt B, ..., apt Z.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
caden311
  • 972
  • 8
  • 22
  • Will the entire list contain either numbers or letters, or will it contain a mixture of both? If it contains a mixture, then what is the expected ordering? – Patrick Roberts Dec 21 '18 at 17:22
  • It can contain a mixture of both. We would want to sort by the letters first and then the numbers. For example [ "unit 1", "unit 2", "unit 11", "apt 1", "apt 2"] should sort to : [apt 1, apt 2, unit 1, unit 2, unit 11]. – caden311 Dec 21 '18 at 17:32
  • Sorry, I think I may have incorrectly worded my question. Can there be a mixture of `['apt A', 'apt B', 'apt 1', 'apt 2']`? – Patrick Roberts Dec 21 '18 at 17:34
  • Yes that is possible. That one should be. [ 'apt 1', 'apt 2', 'apt A', 'apt B',] – caden311 Dec 21 '18 at 17:35
  • This is usually called a "natural sort". Maybe you can find an answer in the existing [natural sort questions](https://stackoverflow.com/questions/tagged/natural-sort) –  Dec 21 '18 at 17:49

1 Answers1

3

Given the clarifications in comments, you can split each string by /(\d+)/g and sort by each element in the resulting string array. Treat even indices as strings, and odd indices as numbers:

const input = ['unit 11', 'unit 1', 'unit 2', 'apt 11', 'apt 1', 'apt 2', 'unit A', 'unit c', 'unit B', 'apt a', 'apt C', 'apt b'];

function customSort (a, b) {
  a = a.toUpperCase().split(/(\d+)/g);
  b = b.toUpperCase().split(/(\d+)/g);

  const length = Math.min(a.length, b.length);

  for (let i = 0; i < length; i++) {
    const cmp = (i % 2)
      ? a[i] - b[i]
      : -(a[i] < b[i]) || +(a[i] > b[i]);

    if (cmp) return cmp;
  }

  return a.length - b.length;
}

console.log(input.sort(customSort));
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153