How to Sort Strings Alphabetically While Accounting for Value When a String Is Numeric

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);
}
}

Sorting a list of strings based on numeric order of numeric part

You could use the re module to split each string into a tuple of characters and grouping the digits into one single element. Something like r'(\d+)|(.)'. The good news with this regex is that it will return separately the numeric and non numeric groups.

As a simple key, we could use:

def key(x):
# the tuple comparison will ensure that numbers come before letters
return [(j, int(i)) if i != '' else (j, i)
for i, j in re.findall(r'(\d+)|(.)', x)]

Demo:

lst = ['a1a', 'a2a', 'a5b', 'a10a', 'b1a', 'abc']
print(sorted(lst, key=key)

gives:

['a1a', 'a2a', 'a5b', 'a10a', 'abc', 'b1a']

If you want a more efficient processing, we could compile the regex only once in a closure

def build_key():
rx = re.compile(r'(\d+)|(.)')
def key(x):
return [(j, int(i)) if i != '' else (j, i)
for i, j in rx.findall(x)]
return key

and use it that way:

sorted(lst, key=build_key())

giving of course the same output.

sort numbers numerically and strings alphabetically in an array perl

Divide into 2 lists, sort each individually, then combine back into 1 list.

use warnings;
use strict;

my @arr = qw(txt text anothertext 38.09 100.87 0.876);

my @word = sort {$a cmp $b} grep { /^[a-z]/i } @arr;
my @num = reverse sort {$a <=> $b} grep { !/^[a-z]/i } @arr;
my @sorted_as = (@word, @num);
print "@sorted_as\n";

Outputs:

anothertext text txt 100.87 38.09 0.876

To get des also, add these lines:

@word = reverse @word;
my @sorted_des = (@word, @num);
print "@sorted_des\n";

Sort a list of strings which contains float numbers

The question arises why you have a string in the first place if you store a double. You should parse them as soon as possible (or prevent that they are strings where you load them).

However, you can always try to parse them afterwards:

List<MyObject> sortedList = myUnsortedList
.OrderyDescending(x => double.Parse(x.Number))
.ToList();

If you dont know if they have all a valid format you can use double.TryParse. Maybe you need to pass the appropriate CultureInfo to double.Parse(for example if your current culture uses comma as decimal separator).

How can I get a sorted `List string ` based on an associated numeric index

It's not working after 10th index.

That is because List().Sort invoke string's comparison function.In string comparison "0" is less than "1", "1" is less than "11" and "12" is less than "2" etc.So it is not working after 10.

You can definition a sample comparison function as below:

 public static int Compare(string a, string b)
{
return int.Parse(a.Substring(0, 15)).CompareTo(int.Parse(b.Substring(0, 15)));
}

and then invoke it in sort method:

stringList.Sort(Compare);

The prerequisite is that your format is satisfied that its first 15 characters can convert to an integer.

Sort on a string that may contain a number

The Alphanum Algorithm

From the website

"People sort strings with numbers differently than software. Most sorting algorithms compare ASCII values, which produces an ordering that is inconsistent with human logic. Here's how to fix it."

Edit: Here's a link to the Java Comparator Implementation from that site.

R: order a vector of strings with both character and numeric values both alphabetically and numerically

EDIT completely change the solution after OP clarification

You can extract the last 3 elements and order, and you create a data.frame:

dat = read.table(text=sub('.*:1:([0-9]+):([0-9]+):([0-9]+)','\\1|\\2|\\3',a),sep='|')
dat
V1 V2 V3
1 1102 14591 91480
2 1102 14592 3881
3 1102 14592 37103
4 1102 14592 37356

Then you order using 3 columns:

 a[with(dat,order(V1,V2,V3))]
[1] "ILLUMINA:420:C2D7UACXX:1:1102:14591:91480" "ILLUMINA:420:C2D7UACXX:1:1102:14592:3881"
[3] "ILLUMINA:420:C2D7UACXX:1:1102:14592:37103" "ILLUMINA:420:C2D7UACXX:1:1102:14592:37356"

C# Sort string array alphabetically taking care of putting strings which start with capital letter first. first

You should specify the comparer, e.g. (Linq solution):

  string[] source = new string[] {
"Lets",
"all",
"go",
"on",
"holiday",
"somewhere",
"very",
"cold",
};

// StringComparer.Ordinal: order by Ascii values; capital letters < small letters
var ordered = source
.OrderBy(item => item, StringComparer.Ordinal);

Console.Write(string.Join(", ", ordered));

Outcome:

  Lets, all, cold, go, holiday, on, somewhere, very

To obtain the desired outcome (in case you insist on ordering), you can put

  var result = string.Join("***", source
.OrderBy(item => item, StringComparer.Ordinal)
.First()
.Select(c => c)); // <- turn string into IEnumerable<char>

Console.Write(result);

Outcome:

  L***e***t***s

In case you want to keep on using your current code, change Array.Sort(s); into

  Array.Sort(s, StringComparer.Ordinal);

LINQ OrderBy or Sort does not order correctly for integer string list?

This particular question comes up all the time and I'm pretty sure there are good duplicate questions here on Stack Overflow covering the topic. Please close the question as a proper duplicate and I'd be more than happy to delete my answer in response.

In the meantime...

The problem is us humans.

We see numbers so we think in numbers. To us, 1 is less than 12, and 12 is less than 100. No problems here.

The problem, however, is that we're asking a computer. And a computer is a bit more finicky. In particular, if we're asking a computer to do a alphabetical sort, it will invariably treat our "things" to sort as text. And this is where the problems occur. Or rather, this is where our expectations no longer match what the computer is going to do.

The computer, when asked to sort strings, sort them as any other string sort, character by character.

Let's look at the items:

1
100
12

To us, the natural sort order would be 1, 12, 100. Increasing order.

To the computer, asked to do a text sort, the natural order is different.

The reason is that it's going to compare strings one character at a time.

Broadly it is going to say "all strings that start with 1 is going to come before strings that start with 2", which means "1" and "100" is going to come before "2". This is exactly the same as saying "all words that start with the letter A comes before words that start with the letter B".

Then it's going to say "all strings that then have a 0 comes before strings that have a 1, 2, 3, etc. up to 9, including space", because this is how text sorting is done.

In other words, when you ask "Sort does not order correctly for integer string list?" the simple answer is "Well, no, because when you sort as text, the integer part is never considered".



Related Topics



Leave a reply



Submit