Given
IList indexes;
ICollection collection;
What is the most elegant way to extract all T in
It seems that the most efficient way would be to use a Dictionary<int,T>
instead of a Collection<T>
. You can still keep a list of indexes you want to use in the IList<int>
.
As I understand it, an ICollection may not necessarily have any order which is why there isn't an extremely elegant solution to access things by index. You many want to consider using a dictionary or list to store the data in the collection.
The best way I can think of is to iterate through the collection while keeping track of what index you are on. Then check if the indexes list contains that index. If so, return that element.
Not elegant, but efficient - make sure indexes are sorted ...
ICollection<T> selected = new Collection<T>();
var indexesIndex = 0;
var collectionIndex = 0;
foreach( var item in collection )
{
if( indexes[indexesIndex] != collectionIndex++ )
{
continue;
}
selected.Add( item );
if( ++indexesIndex == indexes.Count )
{
break;
}
}
Not sure how elegant this is, but here you go.
Since ICollection<>
doesn't give you indexing I just used IEnumerable<>
, and since I didn't need the index on the IList<>
I used IEnumerable<>
there too.
public static IEnumerable<T> IndexedLookup<T>(
IEnumerable<int> indexes, IEnumerable<T> items)
{
using (var indexesEnum = indexes.GetEnumerator())
using (var itemsEnum = items.GetEnumerator())
{
int currentIndex = -1;
while (indexesEnum.MoveNext())
{
while (currentIndex != indexesEnum.Current)
{
if (!itemsEnum.MoveNext())
yield break;
currentIndex++;
}
yield return itemsEnum.Current;
}
}
}
EDIT: Just noticed my solution is similar to Erics.
This assumes that the index sequence is a monotone ascending sequence of non-negative indices. The strategy is straightforward: for each index, bump up an enumerator on the collection to that point and yield the element.
public static IEnumerable<T> GetIndexedItems<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;
}
}
}
}
}
Advantages of this solution over other solutions posted:
Disadvantages:
As a proper answer :
var col = new []{"a","b","c"};
var ints = new []{0,2};
var set = new HashSet<int>(ints);
var result = col.Where((item,index) => set.Contains(index));
A usual with IList.Contains or Enumerable.Contains, don't do lookups in lists if you don't know how many indexes there will be in the collection. Or you'll go the O(n^2) way the hard way. If you want to be on the safe side, you should use a intermediary Lookup/Dictionary/Hashset and test on this collection and not on the vanilla list (linear search is not good for you)