I have a list of CustomClassItem. I have several ints which are the indexes of the items I want to retrieve.
What is the quickest / most efficient way to get them? Something in the spirit of the index operator with more than one index or maybe myList.GetWhereIndexIs(myIntsList)?
You may use Linq:
List<CustomClassItem> items = myIntsList.Select(i => myList[i]).ToList();
Make sure that myIntsList.All(i => i >= 0 && i < myList.Count);
Edit:
If an index doesn't exist in the list, ignore this index:
List<CustomClassItem> items = myIntsList.Where(i => i >= 0 && i < myList.Count)
.Select(i => myList[i]).ToList();
I think a nice and efficient solution would be to use yield in combination with an extension method:
public static IList<T> SelectByIndex<T>(this IList<T> src, IEnumerable<int> indices)
{
foreach (var index in indices) {
yield return src[index];
}
}
Now you can do: myList.SelectByIndex(new [] { 0, 1, 4 });
You could also use a params object:
public static IList<T> SelectByIndexParams<T>(this IList<T> src, params int[] indices)
{
foreach (var index in indices) {
yield return src[index];
}
}
Now you can do: myList.SelectByIndexParams(0, 1, 4);
What you want (if I'm reading correctly) is the following:
var indices = [ 1, 5, 7, 9 ];
list.Where((obj, ind) => indices.Contains(ind)).ToList();
That will give you a List<CustomClassItem> containing all those items whose indices are in your list.
Almost all of the LINQ extension methods accept a function taking a T and an int, that is the index of T in the Enumerable. It's really handy.
Another approach using Enumerable.Join:
var result = myList.Select((Item, Index) => new { Item, Index })
.Join(indices, x => x.Index, index => index, (x, index) => x.Item);
More efficient and safe(ensures that the indices exist) but less readable than other approaches.
Demo
Perhaps you want to create an extension which increases readability and reusability:
public static IEnumerable<T> GetIndices<T>(this IEnumerable<T> inputSequence, IEnumerable<int> indices)
{
var items = inputSequence.Select((Item, Index) => new { Item, Index })
.Join(indices, x => x.Index, index => index, (x, index) => x.Item);
foreach (T item in items)
yield return item;
}
Then you could use it in this way:
var indices = new[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var first5 = myList.GetIndices(indices).Take(5);
Used Take to demonstrate that linq's deferred execution still works here.
Related
So I have a class with an array of values, and a list of those classes.
And I want to return the sum (or any other operation) of all the items in the list, also as an array.
E.g. the sum of {1,2,3}, {10,20,30} & {100,200,300} would be {111,222,333}
So, the resulting array's 1st element will be the sum of all the 1st elements in the input arrays, the 2nd element will be the sum of all the 2nd elements in the input arrays, etc.
I can do it with:
public class Item
{
internal int[] Values = new int[3];
}
public class Items : List<Item>
{
internal int[] Values
{
get
{
int[] retVal = new int[3];
for (int x = 0; x < retVal.Length; x++)
{
retVal[x] = this.Sum(i => i.Values[x]);
}
return retVal;
}
}
}
But I feel that this should be achievable as a single line using LINQ. Is it?
Yes, this can be done using a single linq code line, using Enumrable.Range, Max, Select and Sum:
Notice I've also included a simple condition to save you from an IndexOutOfRangeException should one of the arrays is a different length than the others.
internal int[] ValuesLinq
{
get
{
return Enumerable
.Range(0, this.Max(i => i.Values.Length))
.Select(ind => this.Sum(item => item.Values.Length > ind ? item.Values[ind] : 0))
.ToArray();
}
}
You can see a live demo on Rextester
You can try to Group items withing the arrays by their indexes (so we sum all 1st arrays items, every 2nd items etc.):
int[] retVal = myList
.SelectMany(item => item.Values
.Select((value, index) => new {value, index}))
.GroupBy(item => item.index, item => item.value)
.Select(group => group.Sum())
.ToArray();
This question already has answers here:
Interleaving multiple (more than 2) irregular lists using LINQ
(5 answers)
Closed 5 years ago.
Suppose I have list of list. I want to create new list from given list of list such that elements are in order of example given below.
Inputs:-
List<List<int>> l = new List<List<int>>();
List<int> a = new List<int>();
a.Add(1);
a.Add(2);
a.Add(3);
a.Add(4);
List<int> b = new List<int>();
b.Add(11);
b.Add(12);
b.Add(13);
b.Add(14);
b.Add(15);
b.Add(16);
b.Add(17);
b.Add(18);
l.Add(a);
l.Add(b);
Output(list):-
1
11
2
12
3
13
4
14
15
16
And output list must not contain more than 10 elements.
I am currently doing this using foreach inside while but I want to know how can I do this using LINQ.
int loopCounter = 0,index=0;
List<int> o=new List<int>();
while(o.Count<10)
{
foreach(List<int> x in l)
{
if(o.Count<10)
o.Add(x[index]);
}
index++;
}
Thanks.
Use the SelectMany and Select overloads that receive the item's index. That will be used to apply the desired ordering. The use of the SelectMany is to flatten the nested collections level. Last, apply Take to retrieve only the desired number of items:
var result = l.SelectMany((nested, index) =>
nested.Select((item, nestedIndex) => (index, nestedIndex, item)))
.OrderBy(i => i.nestedIndex)
.ThenBy(i => i.index)
.Select(i => i.item)
.Take(10);
Or in query syntax:
var result = (from c in l.Select((nestedCollection, index) => (nestedCollection, index))
from i in c.nestedCollection.Select((item, index) => (item, index))
orderby i.index, c.index
select i.item).Take(10);
If using a C# 6.0 and prior project an anonymous type instead:
var result = l.SelectMany((nested, index) =>
nested.Select((item, nestedIndex) => new {index, nestedIndex, item}))
.OrderBy(i => i.nestedIndex)
.ThenBy(i => i.index)
.Select(i => i.item)
.Take(10);
To explain why Zip alone is not enough: zip is equivalent to performing a join operation on the second collection to the first, where the
attribute to join by is the index. Therefore Only items that exist in the first collection, if they have a match in the second, will appear in the result.
The next option is to think about left join which will return all items of the first collection with a match (if exists) in the second. In the case described OP is looking for the functionality of a full outer join - get all items of both collection and match when possible.
I know you asked for LINQ, but I do often feel that LINQ is a hammer and as soon as a developer finds it, every problem is a nail. I wouldn't have done this one with LINQ, for a readability/maintainability point of view because I think something like this is simpler and easier to understand/more self documenting:
List<int> r = new List<int>(10);
for(int i = 0; i < 10; i++){
if(i < a.Count)
r.Add(a[i]);
if(i < b.Count)
r.Add(b[i]);
}
You don't need to stop the loop early if a and b collectively only have eg 8 items, but you could by extending the test of the for loop
I also think this case may be more performant than LINQ because it's doing a lot less
If your mandate to use LINQ is academic (this is a homework that must use LINQ) then go ahead, but if it's a normal everyday system that some other poor sucker will have to maintain one day, I implore you to consider whether this is a good application for LINQ
This will handle 2 or more internal List<List<int>>'s - it returns an IEnumerable<int> via yield so you have to call .ToList() on it to make it a list. Linq.Any is used for the break criteria.
Will throw on any list being null. Add checks to your liking.
static IEnumerable<int> FlattenZip (List<List<int>> ienum, int maxLength = int.MaxValue)
{
int done = 0;
int index = 0;
int yielded = 0;
while (yielded <= maxLength && ienum.Any (list => index < list.Count))
foreach (var l in ienum)
{
done++;
if (index < l.Count)
{
// this list is big enough, we will take one out
yielded++;
yield return l[index];
}
if (yielded > maxLength)
break; // we are done
if (done % (ienum.Count) == 0)
index += 1; // checked all lists, advancing index
}
}
public static void Main ()
{
// other testcases to consider:
// in total too few elememts
// one list empty (but not null)
// too many lists (11 for 10 elements)
var l1 = new List<int> { 1, 2, 3, 4 };
var l2 = new List<int> { 11, 12, 13, 14, 15, 16 };
var l3 = new List<int> { 21, 22, 23, 24, 25, 26 };
var l = new List<List<int>> { l1, l2, l3 };
var zipped = FlattenZip (l, 10);
Console.WriteLine (string.Join (", ", zipped));
Console.ReadLine ();
}
I'm trying to track down a bug in our code. I've boiled it down to the snippet below. In the example below I have a grid of ints (a list of rows), but I want to find the indexes of the columns that have a 1. The implementation of this is to create an enumerator for each row and step through each column in turn by keeping the enumerators in step.
class Program
{
static void Main(string[] args)
{
var ints = new List<List<int>> {
new List<int> {0, 0, 1}, // This row has a 1 at index 2
new List<int> {0, 1, 0}, // This row has a 1 at index 1
new List<int> {0, 0, 1} // This row also has a 1 at index 2
};
var result = IndexesWhereThereIsOneInTheColumn(ints);
Console.WriteLine(string.Join(", ", result)); // Expected: "1, 2"
Console.ReadKey();
}
private static IEnumerable<int> IndexesWhereThereIsOneInTheColumn(
IEnumerable<List<int>> myIntsGrid)
{
var enumerators = myIntsGrid.Select(c => c.GetEnumerator()).ToList();
short i = 0;
while (enumerators.All(e => e.MoveNext())) {
if (enumerators.Any(e => e.Current == 1))
yield return i;
i++;
if (i > 1000)
throw new Exception("You have gone too far!!!");
}
}
}
However I have noticed that MoveNext() is not remembered each time around the while loop. MoveNext() always returns true, and Current is always 0. Is this a purposeful feature of Linq to make it more side effect free?
I noticed that this works:
private static IEnumerable<int> IndexesWhereThereIsOneInTheColumn(
IEnumerable<List<int>> myIntsGrid)
{
var enumerators = myIntsGrid.Select(c =>
c.ToArray().GetEnumerator()).ToList(); // added ToArray()
short i = 0;
while (enumerators.All(e => e.MoveNext())) {
if (enumerators.Any(e => (int)e.Current == 1)) // added cast to int
yield return i;
i++;
}
}
So is this just a problem with List?
It is because the enumerator of List<T> is a struct whereas the enumerator of Array is a class.
So when you call Enumerable.All with the struct, copy of enumerator is made and passed as a parameter to Func since structs are copied by value. So e.MoveNext is called on the copy, not the original.
Try this:
Console.WriteLine(new List<int>().GetEnumerator().GetType().IsValueType);
Console.WriteLine(new int[]{}.GetEnumerator().GetType().IsValueType);
It prints:
True
False
As Sriram Sakthivel's answer says the issue is due to lack of boxing and accidentally the list enumerator implementation being a struct, not a reference type. Usually, one would not expect the value-type behavior for an enumerator, as most are either exposed by the IEnumerator/IEnumerator<T> interfaces, or are reference types themselves. A quick way to go around this is to change this line
var enumerators = myIntsGrid.Select(c => c.GetEnumerator()).ToList();
to
var enumerators
= myIntsGrid.Select(c => (IEnumerator) c.GetEnumerator()).ToList();
instead.
The above code will construct a list of already boxed enumerators, which will be treated as reference type instances, because of the interface cast. From that moment on, they should behave as you expect them to in your later code.
If you need a generic enumerator (to avoid casts when latter using the enumerator.Current property), you can cast to the appropriate generic IEnumerator<T> interface:
c => (IEnumerator<int>) c.GetEnumerator()
or even better
c => c.GetEnumerator() as IEnumerator<int>
The as keyword is said to perform a lot better than direct casts, and in the case of a loop it could bring an essential performance benefit. Just be careful that as returns null if the cast fails As per Flater's request from comments:. In the OP's case, it is guaranteed the enumerator implements IEnumerator<int>, so it is safe to go for an as cast.
Alternatively, you could do it with a lambda extension
var ids = Enumerable.Range(0,ints.Max (row => row.Count)).
Where(col => ints.Any(row => (row.Count>col)? row[col] == (1) : false));
or
var ids = Enumerable.Range(0,ints.Max (row=> row.Count)).
Where(col => ints.Any (row => row.ElementAtOrDefault(col) == 1));
Here's a simple implementation using loops and yield:
private static IEnumerable<int> IndexesWhereThereIsOneInTheColumn(
IEnumerable<List<int>> myIntsGrid)
{
for (int i=0; myIntsGrid.Max(l=>l.Count) > i;i++)
{
foreach(var row in myIntsGrid)
{
if (row.Count > i && row[i] == 1)
{
yield return i;
break;
}
}
}
}
Alternatively, use this inside the for loop:
if (myIntsGrid.Any(row => row.Count > i && row[i] == 1)) yield return i;
Just for fun, here's a neat LINQ query that won't cause hard-to-trace side effects in your code:
IEnumerable<int> IndexesWhereThereIsOneInTheColumn(IEnumerable<IEnumerable<int>> myIntsGrid)
{
return myIntsGrid
// Collapse the rows into a single row of the maximum value of all rows
.Aggregate((acc, x) => acc.Zip(x, Math.Max))
// Enumerate the row
.Select((Value,Index) => new { Value, Index })
.Where(x => x.Value == 1)
.Select(x => x.Index);
}
Why can't you just get those indexes like this:
var result = ints.Select (i => i.IndexOf(1)).Distinct().OrderBy(i => i);
Seems to be much easier...
I'm trying to learn LINQ and it seems that finding a series of 'n' elements that match a predicate should be possible but I can't seem to figure out how to approach the problem.
My solution actually needs a second, different predicate to test the 'end' of the sequence but finding the first element that doesn't past a test, after a sequence of at least 5 elements that do pass the test would also be interesting.
Here is my naive non-LINQ approach....
int numPassed = 0;
for (int i = 0; i < array.Count - 1; i++ )
{
if (FirstTest(array[i]))
{
numPassed++;
}
else
{
numPassed = 0;
}
if ((numPassed > 5) && SecondTest(array[i + 1]))
{
foundindex = i;
break;
}
}
A performant LINQ solution is possible but frankly quite ugly. The idea is to isolate subsequences that match the description (a series of N items matching a predicate that ends when an item is found that matches a second predicate) and then select the first of these that has a minimum length.
Let's say that the parameters are:
var data = new[] { 0, 1, 1, 1, 0, 0, 2, 2, 2, 2, 2 };
Func<int, bool> acceptPredicate = i => i != 0;
// The reverse of acceptPredicate, but could be otherwise
Func<int, bool> rejectPredicate = i => i == 0;
Isolating subsequences is possible with GroupBy and a bunch of ugly stateful code (here's the inherent awkwardness -- you have to keep non-trivial state). The idea is to group by an artificial and arbitrary "group number", choosing a different number whenever we move from a subsequence that might be acceptable to one that definitely is not acceptable and when the reverse happens as well:
var acceptMode = false;
var groupCount = 0;
var groups = data.GroupBy(i => {
if (acceptMode && rejectPredicate(i)) {
acceptMode = false;
++groupCount;
}
else if (!acceptMode && acceptPredicate(i)) {
acceptMode = true;
++groupCount;
}
return groupCount;
});
The last step (finding the first group of acceptable length) is easy, but there is one last pitfall: making sure that you don't select one of the groups that do not satisfy the stated condition:
var result = groups.Where(g => !rejectPredicate(g.First()))
.FirstOrDefault(g => g.Count() >= 5);
All of the above is achieved with a single pass over the source sequence.
Note that this code will accept a sequence of items that also ends the source sequence (i.e. it does not terminate because we found an item that satisfies rejectPredicate but because we ran out of data). If you don't want this a slight modification will be required.
See it in action.
Not elegant, but this will work:
var indexList = array
.Select((x, i) => new
{ Item = x, Index = i })
.Where(item =>
item.Index + 5 < array.Length &&
FirstTest(array[item.Index]) &&
FirstTest(array[item.Index+1]) &&
FirstTest(array[item.Index+2]) &&
FirstTest(array[item.Index+3]) &&
FirstTest(array[item.Index+4]) &&
SecondTest(array[item.Index+5]))
.Select(item => item.Index);
Instead of trying to combine existing extension methods, it is much more cleaner to use an Enumerator.
Example:
IEnumerable<T> MatchThis<T>(IEnumerable<T> source,
Func<T, bool> first_predicate,
Int32 times_match,
Func<T, bool> second_predicate)
{
var found = new List<T>();
using (var en = source.GetEnumerator())
{
while(en.MoveNext() && found.Count < times_match)
if (first_predicate((T)en.Current))
found.Add((T)en.Current);
else
found.Clear();
if (found.Count < times_match && !en.MoveNext() || !second_predicate((T)en.Current))
return Enumerable.Empty<T>();
found.Add((T)en.Current);
return found;
}
}
Usage:
var valid_seq = new Int32[] {800, 3423, 423423, 1, 2, 3, 4, 5, 200, 433, 32};
var result = MatchThis(valid_seq, e => e<100, 5, e => e>100);
Result:
var result = array.GetSixth(FirstTest).FirstOrDefault(SecondTest);
internal static class MyExtensions
{
internal static IEnumerable<T> GetSixth<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
var counter=0;
foreach (var item in source)
{
if (counter==5) yield return item;
counter = predicate(item) ? counter + 1 : 0;
}
}
}
It looks like you want continuous 6 elements, the first 5 of which match predicate1, and the last (the 6th) matches predicate2. Your non-linq version works fine, using linq in this case is a little reluctant. And trying to resolve the problem in one linq query makes the issue harder, here is a (maybe) cleaner linq solution:
int continuous = 5;
var temp = array.Select(n => FirstTest(n) ? 1 : 0);
var result = array.Where((n, index) =>
index >= continuous
&& SecondTest(n)
&& temp.Skip(index - continuous).Take(continuous).Sum() == continuous)
.FirstOrDefault();
Things will be easier if you have Morelinq.Batch method.
Like others have mentioned, LINQ is not the ideal solution to this kind of pattern matching need. But still, it is possible, and it doesn't have to be ugly:
Func<int, bool> isBody = n => n == 8;
Func<int, bool> isEnd = n => n == 2;
var requiredBodyLength = 5;
// Index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
int[] sequence = { 6, 8, 8, 9, 2, 1, 8, 8, 8, 8, 8, 8, 8, 2, 5 };
// ^-----------^ ^
// Body End
// First we stick an index on each element, since that's the desired output.
var indexedSequence = sequence.Select((n, i) => new { Index = i, Number = n }).ToArray();
// Scroll to the right to see comments
var patternMatchIndexes = indexedSequence
.Select(x => indexedSequence.Skip(x.Index).TakeWhile(x2 => isBody(x2.Number))) // Skip all the elements we already processed and try to match the body
.Where(body => body.Count() == requiredBodyLength) // Filter out any body sequences of incorrect length
.Select(body => new { BodyIndex = body.First().Index, EndIndex = body.Last().Index + 1 }) // Prepare the index of the first body element and the index of the end element
.Where(x => x.EndIndex < sequence.Length && isEnd(sequence[x.EndIndex])) // Make sure there is at least one element after the body and that it's an end element
.Select(x => x.BodyIndex) // There may be more than one matching pattern, get all their indices
.ToArray();
//patternMatchIndexes.Dump(); // Uncomment in LINQPad to see results
Note that this implementation is not performant at all, it is only meant as a teaching aid to show how something can be done in LINQ despite the unsuitability of solving it that way.
I want to compare two lists with the same number of elements, and find the number of differences between them. Right now, I have this code (which works):
public static int CountDifferences<T> (this IList<T> list1, IList<T> list2)
{
if (list1.Count != list2.Count)
throw new ArgumentException ("Lists must have the same number of elements", "list2");
int count = 0;
for (int i = 0; i < list1.Count; i++) {
if (!EqualityComparer<T>.Default.Equals (list1[i], list2[i]))
count++;
}
return count;
}
This feels messy to me, and it seems like there must be a more elegant way to achieve it. Is there a way, perhaps, to combine the two lists into a single list of tuples, then simple examine each element of the new list to see if both elements are equal?
Since order in the list does count this would be my approach:
public static int CountDifferences<T>(this IList<T> list1, IList<T> list2)
{
if (list1.Count != list2.Count)
throw new ArgumentException("Lists must have the same number of elements", "list2");
int count = list1.Zip(list2, (a, b) => a.Equals(b) ? 0 : 1).Sum();
return count;
}
Simply merging the lists using Enumerable.Zip() then summing up the differences, still O(n) but this just enumerates the lists once.
Also this approach would work on any two IEnumerable of the same type since we do not use the list indexer (besides obviously in your count comparison in the guard check).
I think your approach is fine, but you could use LINQ to simplify your function:
public static int CountDifferences<T>(this IList<T> list1, IList<T> list2)
{
if(list1.Count != list2.Count)
throw new ArgumentException("Lists must have same # elements", "list2");
return list1.Where((t, i) => !Equals(t, list2[i])).Count();
}
The way you have it written in the question, I don't think Intersect does what you're looking for. For example, say you have:
var list1 = new List<int> { 1, 2, 3, 4, 6, 8 };
var list2 = new List<int> { 1, 2, 4, 5, 6, 8 };
If you run list1.CountDifferences(list2), I'm assuming that you want to get back 2 since elements 2 and 3 are different. Intersect in this case will return 5 since the lists have 5 elements in common. So, if you're looking for 5 then Intersect is the way to go. If you're looking to return 2 then you could use the LINQ statement above.
Try something like this:
var result = list1.Intersect(list2);
var differences = list1.Count - result.Count();
If order counts:
var result = a.Where((x,i) => x !=b[i]);
var differences = result.Count();
You want the Intersect extension method of Enumerable.
public static int CountDifferences<T> (this IList<T> list1, IList<T> list2)
{
if (list1.Count != list2.Count)
throw new ArgumentException ("Lists must have the same number of elements", "list2");
return list1.Count - list1.Intersect(list2).Count();
}
You can use the extension method Zip of List.
List<int> lst1 = new List<int> { 1, 2, 3, 4, 5 };
List<int> lst2 = new List<int> { 6, 2, 9, 4, 5 };
int cntDiff = lst1.Zip(lst2, (a, b) => a != b).Count(a => a);
// Output is 2