5

I am trying to sort a string split by comma. But it is not behaving as expected

var classes = "10,7,8,9";
Console.Write(string.Join(",", classes.Split(',').OrderBy(x => x)));
Console.ReadKey();

and output is

10,7,8,9

But I want the expected output to be like:

7,8,9,10

Classes can have a section along with them. like 7a,7b

and I want to achieve it on one line of code.

7
  • I know this probably isn't a solution at all, but couldn't you re-order var classes now so that they appear in the correct order BEFORE splitting them, and if you ever read in dynamic data, do the order by on that query instead? Commented Aug 8, 2017 at 6:38
  • First convert each element of array into integer and try to order Commented Aug 8, 2017 at 6:39
  • @Xariez I am reading data from services so can't perform sorting while querying from database Commented Aug 8, 2017 at 6:42
  • @SPnL classes like 7a, 7b can't be converted to int Commented Aug 8, 2017 at 6:42
  • Why must it be achieved in a single line of code? Commented Aug 8, 2017 at 6:43

6 Answers 6

3

You can use Regex like this

var classes = "10,7,8,9";
Regex number = new Regex(@"^\d+");
Console.Write(string.Join(",", classes.Split(',').OrderBy(x => Convert.ToInt32(number.Match(x).Value)).ThenBy(x => number.Replace(x, ""))));
Console.ReadKey();
Sign up to request clarification or add additional context in comments.

3 Comments

As I Mentioned Classes can have a section along with them. like 7a,7b
what is number here
@tabby forgot to write it! It's fixed now
1

CODE:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Collections;
namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var l = new List<string> { "1D", "25B", "30A", "9C" };
    l.Sort((b, a) =>
    {
        var x = int.Parse(Regex.Replace(a, "[^0-9]", ""));
        var y = int.Parse(Regex.Replace(b, "[^0-9]", ""));
        if (x != y) return y - x;
        return -1 * string.Compare(a, b);
    });

    foreach (var item in l) Console.WriteLine(item);

        }
    }
}

OUTPUT: 1D 9C 25B 30A

ONLINE COMPILE: http://rextester.com/CKKQK66159

1 Comment

I have made a function that I can call which takes string and returning string after sorting classes
1

Use the following using-directive:

using System.Text.RegularExpressions;

And try the following:

var input = "7,7a,8,9a,9c,9d,10";
var sorted = from sp in input.Split(',')
             let reg = Regex.Match(sp, @"(?<num>[0-9]+)(?<char>[a-z]*)", RegexOptions.IgnoreCase | RegexOptions.Compiled)
             let number = int.Parse(reg.Groups["num"].ToString())
             orderby reg.Groups["char"].ToString() ascending // sort first by letter
             orderby number ascending // then by number
             select sp;
var result = string.Join(",", sorted);

Console.WriteLine(result);
//output (tested): 7,7a,8,9a,9c,9d,10

It uses regex to determine the numeric and alphabetic part of the input string. The regex pattern uses named groups, which are noted as follows: (?<group_name> regex_expr ).


The time complexity of the code above is O(n log(n)), in case you are worried about big collections of numbers.


More information about named Regex groups.
More information about LINQ.
... and about the orderby-clause.

2 Comments

it's complex yet it also solves my problem thanks :)
One could reduce the code complexity further, but I thought that this was adequate. Happy to hear that it helped you.
0

All on one line, also supports '4a' etc.

edit: On testing this, a string such as 1,2,111,3 would display as 111,1,2,3, so may not quite be what you're looking for.

        string str = "1,2,3,4a,4b,5,6,4c";
        str.Split(',').OrderBy(x => x).ToList().ForEach(x=> Console.WriteLine(x));
        Console.ReadKey();

Comments

0

Here is my implementation:

IEnumerable<Tuple<string, string[]>> splittedItems =
    items.Select(i => new Tuple<string, string[]>(i, Regex.Split(i, "([0-9]+)")));

List<string> orderedItems = splittedItems.OrderBy(t => Convert.ToInt16(t.Item2[1]))
    .ThenBy(t => t.Item2.Length > 1 ? t.Item2[2] : "1")
    .Select(t => t.Item1).ToList();
  1. Split the input to a number and non numeric characters
  2. Store the splitted strings with their parent string
  3. Order by number
  4. Then order by non numeric characters
  5. Take the parent string again after sorting

The result is like required: { "10", "7", "8b", "8a", "9" } is sorted to { "7", "8a", "8b", "9", "10" }

Comments

0

You are sorting strings (alphabetically), so yes, "10" comes before "7".

My solution converts "10,7b,8,7a,9b" to "7a,7b,8,9b,10" (first sort by the integer prefix, then by the substring itself).

Auxiliary method to parse the prefix of a string:

private static int IntPrefix(string s)
    => s
    .TakeWhile(ch => ch >= '0' && ch <= '9')
    .Aggregate(0, (a, c) => 10 * a + (c - '0'));

Sorting the substrings by the integer prefix then by the string itself:

classes.Split(',') // string[]
.Select(s => new { s, i = IntPrefix(s) }) // IEnumerable<{ s: string, i: int }>
.OrderBy(si => si.i) // IEnumerable<{ s: string, i: int }>
.ThenBy(si => si.s) // IEnumerable<{ s: string, i: int }>
.Select(si => si.s) // IEnumerable<string>

One liner (with string.Join):

var result = string.Join(",", classes.Split(',').Select(s => new {s, i =  IntPrefix(s)}).OrderBy(si => si.i).ThenBy(si => si.s).Select(si => si.s));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.