简体   繁体   中英

C# - Sorting an ArrayList of strings

I have an ArrayList of strings that look as below, I would like to output a new ArrayList sorted in a particular way. But not sure of a good way of sorting it. Help will be really appreciated!

Original (can be in any random order):

1:1
0:0
0:1
2:1
1:0
2:0

Output:

2:0
2:1
1:0
1:1
0:0
0:1

While K Ivanov has the right idea , here's a version that is perhaps easier on the eyes:

// Not sure what to call the "n:n" groupings.  Assume
// original is in an ArrayList named "pairs".
IEnumerable<string> sortedPairs =
    from pair in pairs.Cast<string>()
    let parts = pair.Split(':')
    let parsed = new { 
        Left = Int32.Parse(parts[0]), 
        Right = Int32.Parse(parts[1]),
    }
    orderby parsed.Left descending, parsed.Right
    select pair;

Note that for parsing such as this, a regular expression might be a little overkill (the pattern is very simple, and very clear).

Additionally, it makes the assumption that you have numerics on both sides, as per your example. It also doesn't mutate the strings in any way in returning the result.

this feel wird, but it works based on your requirements, perhaps if you share more of the specifics we can help with more precise solution, there many assumptions, for now try this:

var sortedStrings = new ArrayList(strings.ToArray()
    .Select(s => new { parts = ((String)s).Split(':') })
    .OrderByDescending(p => p.parts[0])
    .ThenBy(p => p.parts[1])
    .Select(p => String.Concat(p.parts[0], ":", p.parts[1])).ToArray());

While I think both of the other answers are spot-on, I'm going to assume you're unfamiliar with some of the features of .NET 2.0 and .NET 3.5 they used. Let's take it one step at a time.

So you're given an ArrayList holding the following data:

{ "1:1", "0:0", "0:1", "2:1", "1:0", "2:0" }

First of all, nothing is wrong with using RegEx; perhaps a minor performance penalty. If the strings are really as simple as this, you can just use Split :

string[] s = myArray[i].Split(new[] { ':' });
int val1 = int.Parse(s[0]);
int val2 = int.Parse(s[1]);

However, since you said you're using .NET 4, you really shouldn't be using ArrayList at all -- note that it requires you to cast your values to their appropriate type, eg string mystring = myArray[i] as string .

There are plenty of great features you're not taking advantage of, such as generics (in the .NET Framework since 2.0). Let's write a function that is given an ArrayList , but returns a sorted generic List<string> (a list that only holds strings). Let's have a look:

/// <summary>
/// This method takes in an ArrayList of unsorted numbers in the format: a:b
/// and returns a sorted List<string> with a descending, b ascending
/// <summary>
public List<string> SortMyValues(ArrayList unsorted)
{
    // Declare an empty, generic List of type 'TwoNumbers'
    List<MyTuple> values = new List<MyTuple>();
    foreach (object item in unsorted)
    {
        char[] splitChar = new char[] { ':' };
        string itemString = item as string;
        string[] s = itemString.Split(splitChar);
        values.Add(new MyTuple{
            FirstNumber = int.Parse(s[0]),
            SecondNumber = int.Parse(s[1])
        });
    }
    // Sort the values
    values.Sort();
    // Return a list of strings, in the format given
    List<string> sorted = new List<string>();
    foreach (MyTuple item in values)
    {
        sorted.Add(item.FirstNumber + ":" + item.SecondNumber);
    }
    return sorted;
}

public class MyTuple : IComparable {
    public int FirstNumber { get; set; }
    public int SecondNumber { get; set; }

    public int CompareTo(object obj)
    {
        if (obj is MyTuple)
        {
            MyTuple other = (MyTuple)obj;

            // First number descending
            if (FirstNumber != other.FirstNumber)
            return other.FirstNumber.CompareTo(FirstNumber);
            // Second number ascending
        return SecondNumber.CompareTo(other.SecondNumber);
        }
        throw new ArgumentException("object is not a MyTuple");
    }
}

Now, the above code works, but is really long. Note that you have to create a class just to hold these two values, make that class implement IComparable, etc, etc. Pretty annoying!

.NET 3.5 came out with some great features, including anonymous types and LINQ . Let's change our code to use both of those features.

/// <summary>
/// This method takes in an ArrayList of unsorted numbers in the format: a:b
/// and returns a sorted List<string> with a descending, b ascending
/// <summary>
public List<string> SortMyValues(ArrayList unsorted)
{
    // First, cast every single element of the given ArrayList to a string
    // The Cast<T> method will do this, and return an enumerable collection
    return unsorted.Cast<string>()
        // Now, let's take this string data and create our objects that will hold two numbers
        .Select(item => {
            // This is the body of an anonymous method with one parameter, which I called 'item'
            // This anonymous method will be executed for every element in the collection
            string[] s = item.Split(new[] { ':' });
            // Here we create a new anonymous type holding our numbers
            // No need to define a new dummy class!
            return new {
                FirstNumber = int.Parse(s[0]),
                SecondNumber = int.Parse(s[1])
            };
        })
        // Now that we've got our objects, let's sort them
        .OrderByDescending(x => x.FirstNumber)
        .ThenBy(x => x.SecondNumber)
        // Finally, now that we're sorted properly, let's format our string list output
        .Select(x => x.FirstNumber + ":" + x.SecondNumber)
        .ToList();
}

Our entire function is just one line now, and most of the code is comments. I encourage you to learn about and start using some of these features; it'll make your code a lot easier to read and write ;-)

Hope this helped!

Edit: In resopnse to your comment:

What would it take to have them in the following order: 2:0 1:0 0:0 2:1 1:1 0:1

It looks like you're sorting by the second number, ascending, and then by the first number, descending.

Simply change the code above to use:

.OrderBy(x => x.SecondNumber)
.ThenByDescending(x => x.FirstNumber)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM