Murtaza Mandvi
Murtaza Mandvi

Reputation: 10998

Order a list based on a fixed format

int[] OrderedListToFollow = {1,2,4,5}
int[] ListB = {2,3,4,8,9}

Based on the two lists above I need to sort ListB based on the order defined in OrderedListToFollow. Since 3,8,9 are not part of the OrderedListToFollow those can appear in any order, so the acceptable solutions could have any of the following :

int[] ListB = {2,4,3,8,9}
int[] ListB = {2,4,8,3,9}
int[] ListB = {2,4,9,3,8}

I tried doing this as follows but it does not order it :

ListB = ListB.OrderBy(id => OrderedListToFollow.ToList().IndexOf(id)).ToArray();

EDIT

The order above works but it places the items not present in OrderedListToFollow first and then the remaining.

Upvotes: 0

Views: 71

Answers (4)

David
David

Reputation: 10708

The problem with your sorting method is that the IndexOf method returns -1 if the item is not found. Thus, all your items which exist "outside" the given ordering scheme are placed at the beginning of the collection because they get an index of -1.

You could try using a conditional to return the index if found and the current index otherwise:

var c = ListB.Count();
ListB = ListB
    .OrderBy(id => OrderedListToFollow.Contains(id)
        ? OrderedListToFollow.ToList().IndexOf(id)
        : c + 1    // This will always pace invalid objects at the end
    );

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1503290

As noted, it was already working apart from putting the results at the beginning. To fix this, I'd add an extension method:

public static int IndexOfOrMax(this IEnumerable<T> source, T item)
{
    int index = source.IndexOf(item);
    return index == -1 ? int.MaxValue : index;
}

Also note that you don't need to call ToList on OrderedListToFollow - currently you're calling that a lot, which is very inefficient. With the above extension method in place, you can use:

int[] orderedListToFollow = {1,2,4,5};
int[] listB = {2,3,4,8,9};
listB = listB.OrderBy(id => orderedListToFollow.IndexOfOrMax(id)).ToArray();

Upvotes: 2

DavidN
DavidN

Reputation: 5197

You can do this using Join and Except, the benefit being that they're both ~O(n) operations (due to both using Hashtables in their implementations). This snippet hinges on the assumption that OrderedListToFollow is indeed ordered.

int[] OrderedListToFollow = new[]{1,2,4,5};
int[] ListB = new[]{3,4,8,2,9};

var existing = from o in OrderedListToFollow
               join l in ListB on o equals l
               select l;

var other = ListB.Except(OrderedListToFollow);

var result = existing.Concat(other);

Upvotes: 0

Aelphaeis
Aelphaeis

Reputation: 2613

How about something like this:

int[] OrderedListToFollow = {1,2,4,5};
int[] ListB = {2,3,4,8,9};


List<int> ListC = new List<int>();
ListC.AddRange(OrderedListToFollow.Where(p => ListB.Contains(p)));
ListC.AddRange(ListB.Where(p => !OrderedListToFollow.Contains(p)));

This will give you a result like this :

2 4 3 8 9

Upvotes: 0

Related Questions