3

What ist the fastest way to iterate a list of elements where each item has an associated "score" and items with hightest score come first.

Example:

List<X> items = new List<X>(new X[]{a,b,c,d});
int[] score = new int[]{20,301,-34,7};
foreach(X x in IterateByScore(items,score)) { // <-- Fastest way to do this?
    // order should be b - a - d - c
}

Edit: The first example used an order list which could be used as indices, but I have a list of "scores". Please excuse the mistake.

Danvil
  • 22,240
  • 19
  • 65
  • 88
  • 1
    Is there any reason why you can't include the ordering value in the definition of "X"? – ChrisF Nov 17 '13 at 19:49
  • @ChrisF: The items and the order come from to different parts of the code which should be kept separate. – Danvil Nov 17 '13 at 19:51

5 Answers5

3

NOTE: This answer was posted before the question was updated, when the values in the second array were the indices in the first.

List<T> uses an array internally, so indexed access is the most reasonable choice here:

foreach (int index in order)
{
    X x = items[index];
    ...
}

If you need, you can encapsulate this indirect indexing logic into an IEnumerator<T> implementation, which would accept List<T> and int[] in its constructor. This approach would be useful in situations where an IEnumerable<T> is expected.

(Personally I would object focusing on performance here unless this is, which I doubt, a performance bottleneck in your program.)

ChrisF
  • 134,786
  • 31
  • 255
  • 325
Ondrej Tucny
  • 27,626
  • 6
  • 70
  • 90
  • Simple, easy, and probably very quick. +1 – Maarten Nov 17 '13 at 19:51
  • Just realized that my example oversimplifies... I wanted to ask something slightly different. Thanks for the answer anyway! – Danvil Nov 17 '13 at 19:53
  • @Danvil Maybe my update regarding the custom `IEnumerator` implementation is pointing in the right direction? – Ondrej Tucny Nov 17 '13 at 19:55
  • @Danvil this should work for you because you use **indices** to specify the order, that's why your example is simple, if you have some other way to define the order (not based on *indices*) then this solution of course won't work. – King King Nov 17 '13 at 19:57
  • @OndrejTucny: Not really... But your answert is totally fine. I am posting a new question. – Danvil Nov 17 '13 at 19:57
  • @KingKing: That is exactly the problem. I am posting a second question. – Danvil Nov 17 '13 at 19:58
  • Dear @downvoter, you should probably pay attention to the NOTE at the beginning of my answer. You will quickly learn this answer was not only relevant, but correct — before the OP changed their question significantly instead of posting a new one. – Ondrej Tucny Nov 18 '13 at 18:44
2

The simple solution would be to create an new class:

public class OrderedX
{
    public X Element { get; set; }
    public int Order { get; set; }
}

Then order that.

X[] element = new X[]{a,b,c,d});
int[] score = new int[]{20,301,-34,7};
List<OrderedX> items = new List<OrderedX>();
for (int i = 0; i < count; i++)
{
    items.Add(new OrderedX
    {
        X = element[i],
        Order = score[i]
    });
}
foreach(OrderedX x in items.OrderBy(a => a.Score)
{
    Process(x.X);
}
Ondrej Tucny
  • 27,626
  • 6
  • 70
  • 90
ChrisF
  • 134,786
  • 31
  • 255
  • 325
1

For the updated question, I assume that the most efficient would be the Array.Sort method:

List<X> items = new List<X>(new X[] { a, b, c, d });
int[] score = new int[] { 20, 301, -34, 7 };
List<X> sorted = items.ToArray();
Array.Sort(score, sorted);

foreach (X x in sorted)
{ 
    // process x here
}
Douglas
  • 53,759
  • 13
  • 140
  • 188
1

I might use Zip and an anonymous type:

foreach (X x in items.Zip(scores, (item, score) => new { item, score })
                          .OrderByDescending(x => x.score).Select(x => x.item))
{
    // order is b - a - d - c
}

If the pairing of X and its score is a common thing, you should create a named class in place of that anonymous type.

Tim S.
  • 55,448
  • 7
  • 96
  • 122
0

Whilst Ondrej's answer is correct, it only holds if you will be iterating over your list once (or a small number of times).

If you need to iterate over the list (in the given order) many times, then it would be more efficient to duplicate the list in the required order first, since this will promote cache hits and significantly improve your memory-access performance.

List<X> items = new List<X>(new X[]{a,b,c,d});
int[] order = new int[] { 2, 3, 1, 0 };
List<X> sorted = order.Select(i => items[i]).ToList();

for (int i = 0; i < 10000000; ++i)
    foreach (X x in sorted)
    {
        // process x here
    }
Douglas
  • 53,759
  • 13
  • 140
  • 188