Sort String-Numbers

Sorting strings containing numbers in a user friendly way

Jeff wrote up an article about this on Coding Horror. This is called natural sorting, where you effectively treat a group of digits as a single "character". There are implementations out there in every language under the sun, but strangely it's not usually built-in to most languages' standard libraries.

Why does sorting strings with numbers have a different result than just sorting the numbers?

Sorting strings is generally done -- across all languages, not just Python -- with the same algorithm, one that operates character-by-character.

  1. Start at the first character of each input string.
    • Is the first character on the left larger than the one on the right? Then decide that the first string as a whole is larger.
    • Is the first character on the right larger than the one on the left? Then decide that the second string as a whole is larger.
    • Are both characters identical? Then we're not done yet.
  2. Move on to the next character and repeat.
    • Is there no next character on one of the two sides? Then the side with the shortest string is smaller.

Following that algorithm, '10' is smaller than '2' because '1' is smaller than '2'.

If you want a result that reflects numeric comparison, you need to compare values as numbers, not as strings.

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

You can sort the list like this:

hi.sort();

(because numbers sort before letters in its implementation)

How to sort string with numbers in it numerically?

I think

val sortedList = data.sortedWith(compareBy(
{ it.listId },
{ it.name.substring(0, it.name.indexOf(' ')) },
{ it.name.substring(it.name.indexOf(' ') + 1).toInt() }
))

will work but it is not computationally efficient because it will call String.indexOf() many times.

If you have a very long list, you should consider making another list whose each item has String and Int names.

How to correctly sort a string with a number inside?

Perhaps you are looking for human sorting (also known as natural sorting):

import re

def atoi(text):
return int(text) if text.isdigit() else text

def natural_keys(text):
'''
alist.sort(key=natural_keys) sorts in human order
http://nedbatchelder.com/blog/200712/human_sorting.html
(See Toothy's implementation in the comments)
'''
return [ atoi(c) for c in re.split(r'(\d+)', text) ]

alist=[
"something1",
"something12",
"something17",
"something2",
"something25",
"something29"]

alist.sort(key=natural_keys)
print(alist)

yields

['something1', 'something2', 'something12', 'something17', 'something25', 'something29']

PS. I've changed my answer to use Toothy's implementation of natural sorting (posted in the comments here) since it is significantly faster than my original answer.


If you wish to sort text with floats, then you'll need to change the regex from one that matches ints (i.e. (\d+)) to a regex that matches floats:

import re

def atof(text):
try:
retval = float(text)
except ValueError:
retval = text
return retval

def natural_keys(text):
'''
alist.sort(key=natural_keys) sorts in human order
http://nedbatchelder.com/blog/200712/human_sorting.html
(See Toothy's implementation in the comments)
float regex comes from https://stackoverflow.com/a/12643073/190597
'''
return [ atof(c) for c in re.split(r'[+-]?([0-9]+(?:[.][0-9]*)?|[.][0-9]+)', text) ]

alist=[
"something1",
"something2",
"something1.0",
"something1.25",
"something1.105"]

alist.sort(key=natural_keys)
print(alist)

yields

['something1', 'something1.0', 'something1.105', 'something1.25', 'something2']

How to sort a list of strings numerically?

You haven't actually converted your strings to ints. Or rather, you did, but then you didn't do anything with the results. What you want is:

list1 = ["1","10","3","22","23","4","2","200"]
list1 = [int(x) for x in list1]
list1.sort()

If for some reason you need to keep strings instead of ints (usually a bad idea, but maybe you need to preserve leading zeros or something), you can use a key function. sort takes a named parameter, key, which is a function that is called on each element before it is compared. The key function's return values are compared instead of comparing the list elements directly:

list1 = ["1","10","3","22","23","4","2","200"]
# call int(x) on each element before comparing it
list1.sort(key=int)
# or if you want to do it all in the same line
list1 = sorted([int(x) for x in list1])

How do I sort strings alphabetically while accounting for value when a string is numeric?

Pass a custom comparer into OrderBy. Enumerable.OrderBy will let you specify any comparer you like.

This is one way to do that:

void Main()
{
string[] things = new string[] { "paul", "bob", "lauren", "007", "90", "101"};

foreach (var thing in things.OrderBy(x => x, new SemiNumericComparer()))
{
Console.WriteLine(thing);
}
}

public class SemiNumericComparer: IComparer<string>
{
/// <summary>
/// Method to determine if a string is a number
/// </summary>
/// <param name="value">String to test</param>
/// <returns>True if numeric</returns>
public static bool IsNumeric(string value)
{
return int.TryParse(value, out _);
}

/// <inheritdoc />
public int Compare(string s1, string s2)
{
const int S1GreaterThanS2 = 1;
const int S2GreaterThanS1 = -1;

var IsNumeric1 = IsNumeric(s1);
var IsNumeric2 = IsNumeric(s2);

if (IsNumeric1 && IsNumeric2)
{
var i1 = Convert.ToInt32(s1);
var i2 = Convert.ToInt32(s2);

if (i1 > i2)
{
return S1GreaterThanS2;
}

if (i1 < i2)
{
return S2GreaterThanS1;
}

return 0;
}

if (IsNumeric1)
{
return S2GreaterThanS1;
}

if (IsNumeric2)
{
return S1GreaterThanS2;
}

return string.Compare(s1, s2, true, CultureInfo.InvariantCulture);
}
}

How do I sort a list of string and numbers in numerical order?

Since your listId is String ,the list gets sorted lexographically. Instead you could first convert the String to an Integer and then sort it. Below is the illustration.

Collections.sort(tempList, (mainData, t1) -> Integer.parseInt(mainData.getListId())-Integer.parseInt(t1.getListId());


Related Topics



Leave a reply



Submit