What is the most elegant way to get a set of items by index from a collection?

后端 未结 14 2024
梦如初夏
梦如初夏 2020-12-13 04:57

Given

IList indexes;
ICollection collection;

What is the most elegant way to extract all T in

相关标签:
14条回答
  • 2020-12-13 06:02

    I find this solution particualarly elegant and a bit easier to follow.

    Solution 1

       public static IEnumerable<T> GetIndexedItems2<T>(this IEnumerable<T> collection,    IEnumerable<int> indices) {
    
            int skipped = 0;
            foreach (int index in indices) {
                int offset = index - skipped;
                collection = collection.Skip(offset);
                skipped += offset;
                yield return collection.First();
            }
        }
    

    This can be refactored farther to a real simple implementation:

    Solution 2

       public static IEnumerable<T> GetIndexedItems3<T>(this IEnumerable<T> collection, IEnumerable<int> indices) {
            foreach (int offset in indices.Distances()) {
                collection = collection.Skip(offset);
                yield return collection.First();
            }
        }
    
        public static IEnumerable<int> Distances(this IEnumerable<int> numbers) {
            int offset = 0;
            foreach (var number in numbers) {
                yield return number - offset;
                offset = number;
            }
        }
    

    But we are not done

    Due to deferred execution LINQs Skip is way too slow.

       public static IEnumerable<T> GetIndexedItems4<T>(this IEnumerable<T> collection, IEnumerable<int> indices) {
            var rest = collection.GetEnumerator();
            foreach (int offset in indices.Distances()) {
                Skip(rest, offset);
                yield return rest.Current;
            }
        }
    
        static void Skip<T>(IEnumerator<T> enumerator, int skip) {
            while (skip > 0) {
                enumerator.MoveNext();
                skip--;
            }
            return;
        }
    
        static IEnumerable<int> Distances(this IEnumerable<int> numbers) {
            int offset = 0;
            foreach (var number in numbers) {
                yield return number - offset;
                offset = number;
            }
        }
    

    Benchmarking, gives us similar performance to the solution by Eric.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Diagnostics;
    
    namespace ConsoleApplication21 {
    
        static class LinqExtensions {
    
            public static IEnumerable<T> GetIndexedItemsEric<T>(this IEnumerable<T> collection, IEnumerable<int> indices) {
                int currentIndex = -1;
                using (var collectionEnum = collection.GetEnumerator()) {
                    foreach (int index in indices) {
                        while (collectionEnum.MoveNext()) {
                            currentIndex += 1;
                            if (currentIndex == index) {
                                yield return collectionEnum.Current;
                                break;
                            }
                        }
                    }
                }
            }
    
            public static IEnumerable<T> GetIndexedItemsSam<T>(this IEnumerable<T> collection, IEnumerable<int> indices) {
                var rest = collection.GetEnumerator();
                foreach (int offset in indices.Distances()) {
                    Skip(rest, offset);
                    yield return rest.Current;
                }
            }
    
            static void Skip<T>(this IEnumerator<T> enumerator, int skip) {
                while (skip > 0) {
                    enumerator.MoveNext();
                    skip--;
                }
                return;
            }
    
            static IEnumerable<int> Distances(this IEnumerable<int> numbers) {
                int offset = 0;
                foreach (var number in numbers) {
                    yield return number - offset;
                    offset = number;
                }
            }
        } 
    
        class Program {
    
            static void TimeAction(string description, int iterations, Action func) {
                var watch = new Stopwatch();
                watch.Start();
                for (int i = 0; i < iterations; i++) {
                    func(); 
                }
                watch.Stop();
                Console.Write(description);
                Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
            }
    
            static void Main(string[] args) {
    
                int max = 100000;
                int lookupCount = 1000;
                int iterations = 500;
                var rand = new Random();
                var array = Enumerable.Range(0, max).ToArray();
                var lookups = Enumerable.Range(0, lookupCount).Select(i => rand.Next(max - 1)).Distinct().OrderBy(_ => _).ToArray();
    
                // warmup 
                array.GetIndexedItemsEric(lookups).ToArray();
                array.GetIndexedItemsSam(lookups).ToArray();
    
                TimeAction("Eric's Solution", iterations, () => {
                    array.GetIndexedItemsEric(lookups).ToArray();
                });
    
                TimeAction("Sam's Solution", iterations, () =>
                {
                    array.GetIndexedItemsEric(lookups).ToArray();
                });
    
                Console.ReadKey();
            }
        }
    }
    
     
    Eric's Solution Time Elapsed 770 ms
    Sam's Solution Time Elapsed 768 ms
    
    0 讨论(0)
  • 2020-12-13 06:03

    I like linq.

        IList<T> list = collection.ToList<T>();
    
        var result = from i in indexes
                     select list[i];
    
        return result.ToList<T>();
    
    0 讨论(0)
提交回复
热议问题