Reputation: 2149
I have a List
and I have the new order in which the List
should have the items in int[]
I want that the item in List
should be re-ordered as per the items in int[]
. Here is my code to do that:
class Program
{
static void Main(string[] args)
{
List<Test> tests = new List<Test>() {
new Test(){ No = 201 },
new Test(){ No = 101 },
new Test(){ No = 300 },
new Test(){ No = 401 },
new Test(){ No = 500 },
new Test(){ No = 601 }
};
int[] newOrder = new int[6] { 201, 401, 300, 101, 601, 500 };
//after the opration the List should contain items in order 201, 401, 300, 101, 601, 500
List<Test> newTests = new List<Test>();
foreach(var order in newOrder)
{
var item = tests.SingleOrDefault(t => t.No == order);
if (item != null)
newTests.Add(item);
}
}
}
This works fine. But it creates a separate List
and performs operation on it. Is there better way where I can use may be built in .Net operation for this or may be perform the operation on the same List
without creating these Temp List
etc?
Thank you.
Upvotes: 6
Views: 8852
Reputation: 5743
Using left join can be one of the way to use the customized ordering. For any item not match to the customized ordering, use original order of the list starting at the end
var results = from a in tests.Select((r, i) => new {item = r, Index = i})
// left join
from b in newOrder.Select((r, i) => new { item = r, Index = i })
.Where(b => a.item.No == b.item).DefaultIfEmpty()
// Not in order list then use original ordering
orderby (b == null ? tests.Count() + a.Index : b.Index)
select a.item;
Upvotes: 2
Reputation: 4036
You need to think about performance when running a sort like this.
If you expect only a handful of elements, then Pedro's solution is OK.
If you expect to have many elements (say, 100's or 1000's), then it's not a good idea to search through entire collection of tests
for each element in newOrder
. In this case, it would be helpful to use a Dictionary
for all index/sort-order lookups. Try something like this:
List<Test> tests = new List<Test>() {
new Test(){ No = 101 },
new Test(){ No = 201 },
new Test(){ No = 300 },
new Test(){ No = 401 },
new Test(){ No = 500 },
new Test(){ No = 601 }
};
int[] newOrder = new int[6] { 201, 401, 300, 101, 601, 500 };
// Create a Dictionary/hashtable so we don't have to search in newOrder repeatedly
// It will look like this: { {201,0}, {401,1}, {300,2}, {101,3}, {601,4}, {500,5} }
Dictionary<int, int> newOrderIndexedMap = Enumerable.Range(0, newOrder.Length - 1).ToDictionary(r => newOrder[r], r => r);
// Order using 1 CPU
var orderedTests = tests.OrderBy(test => newOrderIndexedMap[test.No]);
// Order using multi-threading
var orderedInParallelTests = tests.AsParallel().OrderBy(test => newOrderIndexedMap[test.No]);
// Order using 1 CPU, when it's possible that a match will not be found in newOrder
var orderedTestsSafe = tests.OrderBy(test =>
{
int index;
bool foundIndex = newOrderIndexedMap.TryGetValue(test.No, out index);
return foundIndex ? index : Int32.MaxValue;
});
Note that both this answer and Pedro's assume that newOrder
contains all values contained in the tests
elements and vice versa.
Upvotes: 9
Reputation: 1949
Try joining your array and list like this
newOrder.Join(tests, no => no, tst => tst.No, (no, tst) => tst)
Upvotes: 1
Reputation: 3222
var newTesties= newOrder.Select(o => tests.First(t => t.No == o));
Basically, I'm selecting every number 'o' in newOrder, and using that to pick up the corresponding test. You WILL end up with a new list though.
Upvotes: 2