1

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
6
  • @Mike - Would it be possible to store the information as objects instead of strings? i.e. do they NEED to be strings, or only when they're output? Commented Apr 3, 2011 at 2:50
  • @Pandincus: I would like to output them again as sorted strings. But if that makes it easier I guess that would be fine. Commented Apr 3, 2011 at 2:53
  • @Mike - Additionally, what version of the .NET Framework are you using? Commented Apr 3, 2011 at 2:56
  • .NET Framework version 4 Commented Apr 3, 2011 at 2:58
  • 4
    Stop using ArrayList list please. Since .net 2, you can use a strong-typed generic list: List<string> Commented Apr 3, 2011 at 3:16

3 Answers 3

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

+1 - this was very close to what I came up with. @Mike: You can do a ToList() on the result to get a List<string>. Replace ArrayList with its generic equivalent (List<T>).
2

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

1 Comment

+1 works fine: I would change last line with : .Select(p => string.Format("{0}:{1}", p.Parts[0], p.Parts[1])).ToArray(); and the cast to string is not required (at least in VS 2010)
1

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, e.g. 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)

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.