'Is there a way to sort string lists by numbers inside of the strings?

Is there a way to sort something like:

List<String> hi = ['1hi', '2hi','5hi', '3hi', '4hi'];

to this?

['1hi', '2hi','3hi', '4hi', '5hi']


Solution 1:[1]

Just calling List<String>.sort() by itself will do a lexicographic sort. That is, your strings will be sorted in character code order, and '10' will be sorted before '2'. That usually isn't expected.

A lexicographic sort will work if your numbers have leading 0s to ensure that all numbers have the same number of digits. However, if the number of digits is variable, you will need to parse the values of the numbers for sorting. A more general approach is to provide a callback to .sort() to tell it how to determine the relative ordering of two items.

Luckily, package:collection has a compareNatural function that can do this for you:

import 'package:collection/collection.dart';

List<String> hi = ['1hi', '2hi','5hi', '3hi', '4hi'];
hi.sort(compareNatural);

If your situation is a bit more complicated and compareNatural doesn't do what you want, a more general approach is to make the .sort() callback do parsing itself, such as via a regular expression:

/// Returns the integer prefix from a string.
///
/// Returns null if no integer prefix is found.
int parseIntPrefix(String s) {
  var re = RegExp(r'(-?[0-9]+).*');
  var match = re.firstMatch(s);
  if (match == null) {
    return null;
  }
  return int.parse(match.group(1));
}

int compareIntPrefixes(String a, String b) {
  var aValue = parseIntPrefix(a);
  var bValue = parseIntPrefix(b);
  if (aValue != null && bValue != null) {
    return aValue - bValue;
  }

  if (aValue == null && bValue == null) {
    // If neither string has an integer prefix, sort the strings lexically.
    return a.compareTo(b);
  }

  // Sort strings with integer prefixes before strings without.
  if (aValue == null) {
    return 1;
  } else {
    return -1;
  }
}

void main() {
  List<String> hi = ['1hi', '2hi','5hi', '3hi', '4hi'];
  hi.sort(compareIntPrefixes);
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1