Operation on overlapping elements on IEnumerable by LINQ - c#

Let us consider an IEnumerable and the algorithm that takes pairs of overlapping indexes e.g. {0, 1}, {1, 2}, {2, 3} etc. end creates a new collection based on values of these indexes e.g. {collection[0], collection[1] => result[0]}, {collection[1], collection[2] => result[1]} and so on. Below is an example of straight implementation:
IEnumerable<string> collection = new string[100];
var array = collection.ToArray();
var results = array.Skip(1).Select((e, i) => e - array[i]);
How to achieve the goal in better manner?

var array = new string[] { "one", "two", "three" };
var result = Enumerable.Range(1, array.Length - 1)
.Select(i => new[] { array[i - 1], array[i] });
Here is #TrustMe solution with arrays instead of tuples (just to show you sample, you should not accept my answer):
IEnumerable<string> collection = new string[] { "one", "two", "three" };
var result = collection.Zip(collection.Skip(1), (x,y) => new [] { x, y });
But keep in mind, that collection will be enumerated two times if you do not use access by index (with array or list).
UPDATE Here is an extension method, which will work with collection and will enumerate sequence only once:
public static class Extensions
{
public static IEnumerable<T[]> GetOverlappingPairs<T>(
this IEnumerable<T> source)
{
var enumerator = source.GetEnumerator();
enumerator.MoveNext();
var first = enumerator.Current;
while (enumerator.MoveNext())
{
var second = enumerator.Current;
yield return new T[] { first, second };
first = second;
}
}
}
Usage:
var result = collection.GetOverlappingPairs();

And here's another one:
var ints = Enumerable.Range(0, 10);
var paired = ints.Zip(ints.Skip(1), Tuple.Create);
That way you'll get the pairs {0,1}, {1,2} ...
I assume that's what you're asking for, because your code sample is a tad different than what you described... :)

var result = Enumerable.Range(1, arrayCollection.Length - 1)
.Select(i => new[] {arrayCollection[i - 1], arrayCollection[i]});
If arrayCollection is IEnumerable
var result = Enumerable.Range(1, arrayCollection.Count() - 1)
.Select(i => new[] {
arrayCollection.ElementAt(i - 1),
arrayCollection.ElementAt(i)
});

Related

Join 2 lists have different length by in LINQ

How can I join 2 lists of different lengths. it should join with the sequence.
Eg.
{1,2,3,4} with {5,6,7}
I need to get result like below.
{{1,5}, {2,6}, {3,7}, {4,null}}
I tried this.
var qry = a.Select((i, index) => new {i, j = b[index]});
But its throwing error since the lists are having different lengths.
Please help me to get the solution.
This should work:
var a = new int?[] { 1, 2, 3, 4 };
var b = new int?[] { 5, 6, 7 };
var result = Enumerable.Range(0, Math.Max(a.Count(), b.Count()))
.Select(n => new[] {a.ElementAtOrDefault(n), b.ElementAtOrDefault(n)});
Do note the ? in the array declarations. That is necessary in order to have null values in the resulting list. Omitting the ? causes the result to have 0 instead of null.
If you can't or don't want to declare the arrays as int?, then you'll have to do the cast in the Select like so:
var result = Enumerable.Range(0, Math.Max(a.Count(), b.Count()))
.Select(n => new[] { a.Select(i => (int?)i).ElementAtOrDefault(n), b.Select(i => (int?)i).ElementAtOrDefault(n) });
This second bit of code will work correctly with regular int arrays or Lists.
The ugly but working version is the following:
a.Cast<int?>().Concat(Enumerable.Repeat<int?>(null, Math.Max(b.Count() - a.Count(), 0)))
.Zip(b.Cast<int?>()
.Concat(Enumerable.Repeat<int?>(null, Math.Max(a.Count() - b.Count(), 0))),
(x, y) => new { x, y });
Its drawback it double evaluation of a collection (the first one is by calling .Count()).
So it is better just to write an extension
static IEnumerable<TResult> ZipNull<T1, T2, TResult>(this IEnumerable<T1> a, IEnumerable<T2> b, Func<T1?, T2?, TResult> func)
where T1 : struct
where T2 : struct
{
using (var it1 = a.GetEnumerator())
using (var it2 = b.GetEnumerator())
{
while (true)
{
if (it1.MoveNext())
{
if (it2.MoveNext())
{
yield return func(it1.Current, it2.Current);
}
else
{
yield return func(it1.Current, null);
}
}
else
{
if (it2.MoveNext())
{
yield return func(null, it2.Current);
}
else
{
break;
}
}
}
}
}
and use it as
a.ZipNull(b, (x, y) => new { x, y });
What you have is effectively a Zip, but where it zips to the end of the longer, rather than the shorter, of the two sequences. You can write such a Zip method, with something that looks a bit similar to the actual Zip implementation:
public static IEnumerable<TResult> ZipAll<TSource, TSecond, TResult>(this IEnumerable<TSource> source,
IEnumerable<TSecond> other,
Func<TSource, TSecond, TResult> projection)
{
using (var firstIterator = source.GetEnumerator())
using (var secondIterator = other.GetEnumerator())
{
while (true)
{
bool hasFirst = firstIterator.MoveNext();
bool hasSecond = secondIterator.MoveNext();
TSource first = hasFirst ? firstIterator.Current : default(TSource);
TSecond second = hasSecond ? secondIterator.Current : default(TSecond);
if (hasFirst || hasSecond)
yield return projection(first, second);
else
yield break;
}
}
}
With that you can write:
a.ZipAll(b, (i, j) => new { i, j });
You could make the code a bit shorter by requiring the inputs to be lists, but the code wouldn't be any faster as lists, just less typing, and it's not like it's that much extra work to support any sequence, so I'd say it's worth the added few lines of code.
Simply loop through the lists and construct new, let's say Dictionary<int?, int?> out of each list element:
var theFirstList = new List<int?> { 1, 2, 3, 4 };
var theSecondList = new List<int?> { 5, 6, 7 };
var el = new Dictionary<int?, int?>();
var length = Math.Max(theFirstList.Count, theSecondList.Count);
for (int i = 0; i < length; i++)
{
el.Add(theFirstList.ElementAtOrDefault(i), theSecondList.ElementAtOrDefault(i));
}
var x = new[] { 1, 2, 3, 4 }.ToList();
var y = new[] { 5, 6, 7 }.ToList();
var arrayLists = new[] {x, y}.OrderBy(t => t.Count).ToList();
var result = arrayLists
.Last()
.Select((item, i) => new[] { x[i], i < arrayLists.First().Count ? y[i] : (int?)null })
.ToList();
this should work for any IEnumerable

Merge intersecting items in a list of lists

I have list of lists where i would like to merge all lists which contain identical values and make a new list out of the merged lists using linq. Here's and example:
var animalGroups = new List<List<Animal>>{
new List<Animal>{lizard,cat,cow,dog},
new List<Animal>{horse, chicken, pig, turkey},
new List<Animal>{ferret,duck,cat,parrot},
new List<Animal>{chicken,sheep,horse,rabbit}
};
The desired output would be a new List<List<animal>> containing the following List<Animal>:
{lizard, cat, cow, dog, ferret, duck, parrot}
{horse, chicken, pig, turkey, sheep, rabbit}
I'm rather new to linq and i got stuck at grouping the intersected lists without creating duplicates.
Here is the possible output with list of strings
var animalGroups = new List<List<string>>
{
new List<string> {"lizard", "cat", "cow", "dog"},
new List<string> {"horse", "chicken", "pig", "turkey"},
new List<string> {"ferret", "duck", "cat", "parrot"},
new List<string> {"chicken", "sheep", "horse", "rabbit"}
};
List<List<string>> mergedList = new List<List<string>>();
for (int i = 0; i < animalGroups.Count; i++)
{
for (int j = i+1; j < animalGroups.Count; j++)
{
if (animalGroups[i].Intersect(animalGroups[j]).Any())
{
mergedList.Add(animalGroups[i].Concat(animalGroups[j]).Distinct().ToList());
}
}
}
First, remember to override Equals and GetHahCode and/or implement IEquatable<Animal> in your Anymial class meaningfully(f.e. by comparing the Name).
List<IEnumerable<Animal>> mergedLists = animalGroups.MergeIntersectingLists().ToList();
Following extension method used which works with any type:
public static IEnumerable<IEnumerable<T>> MergeIntersectingLists<T>(this IEnumerable<IEnumerable<T>> itemLists, IEqualityComparer<T> comparer = null)
{
if (comparer == null) comparer = EqualityComparer<T>.Default;
var itemListDict = new Dictionary<T, HashSet<T>>(comparer);
foreach (IEnumerable<T> sequence in itemLists)
{
IList<T> list = sequence as IList<T> ?? sequence.ToList();
HashSet<T> itemStorage = null;
list.FirstOrDefault(i => itemListDict.TryGetValue(i, out itemStorage));
// FirstOrDefault will initialize the itemStorage because its an out-parameter
bool partOfListIsContainedInOther = itemStorage != null;
if (partOfListIsContainedInOther)
{
// add this list to the other storage (a HashSet that removes duplicates)
foreach (T item in list)
itemStorage.Add(item);
}
else
{
itemStorage = new HashSet<T>(list, comparer);
// each items needs to be added to the dictionary, all have the same storage
foreach (T item in itemStorage)
itemListDict.Add(item, itemStorage); // same storage for all
}
}
// Distinct removes duplicate HashSets because of reference equality
// needed because item was the key and it's storage the value
// and those HashSets are the same reference
return itemListDict.Values.Distinct();
}
Your question is vague one; in case you want to combine 0, 2, 4, ... 2n as well as 1, 3, 5, ... 2n - 1 lists and you are looking for Linq solution:
// I don't have Animal class, that's why I've put string
// Be sure that Animal implements Equals as well as GetHashCode methods
var animalGroups = new List<List<string>> {
new List<string> {"lizard", "cat", "cow", "dog"},
new List<string> {"horse", "chicken", "pig", "turkey"},
new List<string> {"ferret", "duck", "cat", "parrot"},
new List<string> {"chicken", "sheep", "horse", "rabbit"}
};
var result = animalGroups
.Select((list, index) => new {
list = list,
index = index, })
.GroupBy(item => item.index % 2, // grouping 0, 2, ... 2n as well as 1, 3,... 2n - 1
item => item.list)
.Select(chunk => chunk
.SelectMany(c => c)
.Distinct()
.ToList())
.ToList();
Let's visualize the result:
string test = string.Join(Environment.NewLine, result
.Select(list => string.Join(", ", list)));
Console.WritelLine(test);
Outcome
lizard, cat, cow, dog, ferret, duck, parrot
horse, chicken, pig, turkey, sheep, rabbit

c# filtering list of list of integers if empty

I have a list of list of integers. How do I filter it of the list of integer inside list is empty.
For example:- L = [[0,1,2], [], [1], [1,2,3]]. How do I filter it to get L = [[0,1,2], [1], [1,2,3]] using linq?
Here [0,1,2] is list of integers.
This is related to my previous question. I have a recursive function which returns a list of list of integers and I need to filter out the empty lists.
int [] num_list = new int[10]{2, 3, 5, 9, 14, 19, 23, 45, 92, 100};
public List<List<int>> find_solutions(int n, int t, int w)
{
if (n == 2)
{
List<int> s = new List<int>();
for (var i=0; i <= t; i++)
{
if (i * num_list[1] + (t - i) * num_list[0] == w)
{
s.Add(i);
s.Add(t - i);
}
}
return new List<List<int>> { s };
}
List<List<int>> f = new List<List<int>>();
List<List<int>> temp_list = new List<List<int>>();
for (int i=0; i <= Math.Min(t, w/num_list[n-1]); i++)
{
temp_list = find_solutions(n - 1, t - i, w - i * num_list[n - 1]);
// I strongly believe that i am getting empty list
// and I should filter out empty list form temp_list
// and I need to insert 'i' to each list inside list
foreach(List<int> c in temp_list)
{
c.Insert(0, i);
}
f.addRange(temp_list);
}
return f;
}
int[][] allLists = new int[][] { new int[] { 0, 1, 2 }, new int[] { },
new int[] { 1 }, new int[] { 1, 2, 3 } };
int[][] nonEmtpy = allLists.Where(list => list.Any()).ToArray();
You can use Where and Any to filter out any empty entries.
var listOfLists = L; //To use a better name for L
var nonEmptyLists = listOfLists.Where(innerList => innerList.Any());
You can iterate over this, and/or call ToList or ToArray if necessary
You can achieve this in following ways:
L.Where(p=>p.Any()).ToList();
L.Where(p=>p.Count > 0).ToList();
The Count property is optimized for ICollection<T> where T is a type. Any() has to build an enumerator. So the Count property is faster than Any() method
You can filter using Where() and asking for Count:
var list = new List<List<int>>();
...
list = list.Where(l => l.Count > 0).ToList();
Like this?
L.Where(i => i.Count() > 0).ToList()

Combine entries from two lists by position using LINQ

Say I have two lists with following entries
List<int> a = new List<int> { 1, 2, 5, 10 };
List<int> b = new List<int> { 6, 20, 3 };
I want to create another List c where its entries are items inserted by position from two lists. So List c would contain the following entries:
List<int> c = {1, 6, 2, 20, 5, 3, 10}
Is there a way to do it in .NET using LINQ? I was looking at .Zip() LINQ extension, but wasn't sure how to use it in this case.
Thanks in advance!
To do it using LINQ, you can use this piece of LINQPad example code:
void Main()
{
List<int> a = new List<int> { 1, 2, 5, 10 };
List<int> b = new List<int> { 6, 20, 3 };
var result = Enumerable.Zip(a, b, (aElement, bElement) => new[] { aElement, bElement })
.SelectMany(ab => ab)
.Concat(a.Skip(Math.Min(a.Count, b.Count)))
.Concat(b.Skip(Math.Min(a.Count, b.Count)));
result.Dump();
}
Output:
This will:
Zip the two lists together (which will stop when either runs out of elements)
Producing an array containing the two elements (one from a, another from b)
Using SelectMany to "flatten" this out to one sequence of values
Concatenate in the remainder from either list (only one or neither of the two calls to Concat should add any elements)
Now, having said that, personally I would've used this:
public static IEnumerable<T> Intertwine<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
using (var enumerator1 = a.GetEnumerator())
using (var enumerator2 = b.GetEnumerator())
{
bool more1 = enumerator1.MoveNext();
bool more2 = enumerator2.MoveNext();
while (more1 && more2)
{
yield return enumerator1.Current;
yield return enumerator2.Current;
more1 = enumerator1.MoveNext();
more2 = enumerator2.MoveNext();
}
while (more1)
{
yield return enumerator1.Current;
more1 = enumerator1.MoveNext();
}
while (more2)
{
yield return enumerator2.Current;
more2 = enumerator2.MoveNext();
}
}
}
Reasons:
It doesn't enumerate a nor b more than once
I'm skeptical about the performance of Skip
It can work with any IEnumerable<T> and not just List<T>
I'd create an extension method to do it.
public static List<T> MergeAll<T>(this List<T> first, List<T> second)
{
int maxCount = (first.Count > second. Count) ? first.Count : second.Count;
var ret = new List<T>();
for (int i = 0; i < maxCount; i++)
{
if (first.Count < maxCount)
ret.Add(first[i]);
if (second.Count < maxCount)
ret.Add(second[i]);
}
return ret;
}
This would iterate through both lists once. If one list is bigger than the other it will continue to add until it's done.
You could try this code:
List<int> c = a.Select((i, index) => new Tuple<int, int>(i, index * 2))
.Union(b.Select((i, index) => new Tuple<int, int>(i, index * 2 + 1)))
.OrderBy(t => t.Second)
.Select(t => t.First).ToList();
It makes a union of two collections and then sorts that union using index. Elements from the first collection have even indices, from the second - odd ones.
Just wrote a little extension for this:
public static class MyEnumerable
{
public static IEnumerable<T> Smash<T>(this IEnumerable<T> one, IEnumerable<T> two)
{
using (IEnumerator<T> enumeratorOne = one.GetEnumerator(),
enumeratorTwo = two.GetEnumerator())
{
bool twoFinished = false;
while (enumeratorOne.MoveNext())
{
yield return enumeratorOne.Current;
if (!twoFinished && enumeratorTwo.MoveNext())
{
yield return enumeratorTwo.Current;
}
}
if (!twoFinished)
{
while (enumeratorTwo.MoveNext())
{
yield return enumeratorTwo.Current;
}
}
}
}
}
Usage:
var a = new List<int> { 1, 2, 5, 10 };
var b = new List<int> { 6, 20, 3 };
var c = a.Smash(b); // 1, 6, 2, 20, 5, 3, 10
var d = b.Smash(a); // 6, 1, 20, 2, 3, 5, 10
This will work for any IEnumerable so you can also do:
var a = new List<string> { "the", "brown", "jumped", "the", "lazy", "dog" };
var b = new List<string> { "quick", "dog", "over" };
var c = a.Smash(b); // the, quick, brown, fox, jumped, over, the, lazy, dog
You could use Concat and an anonymous type which you order by the index:
List<int> c = a
.Select((val, index) => new { val, index })
.Concat(b.Select((val, index) => new { val, index }))
.OrderBy(x => x.index)
.Select(x => x.val)
.ToList();
However, since that's not really elegant and also less efficient than:
c = new List<int>(a.Count + b.Count);
int max = Math.Max(a.Count, b.Count);
int aMax = a.Count;
int bMax = b.Count;
for (int i = 0; i < max; i++)
{
if(i < aMax)
c.Add(a[i]);
if(i < bMax)
c.Add(b[i]);
}
I wouldn't use LINQ at all.
Sorry for adding a third extension method inspired by the other two, but I like it shorter:
static IEnumerable<T> Intertwine<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
using (var enumerator1 = a.GetEnumerator())
using (var enumerator2 = b.GetEnumerator()) {
bool more1 = true, more2 = true;
do {
if (more1 && (more1 = enumerator1.MoveNext()))
yield return enumerator1.Current;
if (more2 && (more2 = enumerator2.MoveNext()))
yield return enumerator2.Current;
} while (more1 || more2);
}
}

Select indexes of all elements in sequence

Using LINQ, can I write a statement that will return an IEnumerable of the indexes of items.
Very simple instance:
{1,2,4,5,3}
would just return
{0,1,2,3,4}
and
{1,2,4,5,3}.Where(num => num == 4)
would return
{2}
It isn't exact code, but it should get the idea across.
var a = new[] {1, 2, 4, 5, 3};
//** First, generates a simple sequence with {0,1,2,3,4}
//** using the 2 parameter lambda select
var sequence1 = a.Select((_, index) => index);
//** Second, gets an array with all indexes where the value is 4.
// We need both value and index for the next line to work.
var sequence2 = a.Select((value, index) => new {value, index});
// Get all indexes where the value is 4
var indexArray = sequence2.Where(x => x.value == 4)
.Select(x => x.index).ToArray();
var numbers = Enumerable.Range(1,10).ToList();
int index = -1;
var indices = numbers.Select(x => i++).ToList();
If you're willing to change up your syntax a bit and use an extension method, the following will work. I'm not keen on it as it creates a new sequence for every call.
var sequence = new[] { 1, 2, 4, 5, 3 };
sequence.Indexer().Select(num => num.Item1); // returns {0,1,2,3,4}
sequence.Indexer().Where(num => num.Item2 == 4).Select(num => num.Item1); // returns {2}
private static IEnumerable<Tuple<int, T>> Indexer<T>(this IEnumerable<T> sequence)
{
return sequence.Select((x, y) => new Tuple<int, T>(y, x));
}
A better way would be to change up the way you're writing it altogether:
var sequence = new[] { 1, 2, 4, 5, 3 };
sequence.Select((num, index) => new { Num = num, Index = index }).Select(num => num.Index); // returns {0, 1,2,3,4}
sequence.Select((num, index) => new { Num = num, Index = index }).Where(num => num.Num == 4).Select(num => num.Index); // returns {2}
The full set of indices just depends on the number of items, not on the values, so you can do this:
IEnumerable<int> indices = Enumerable.Range(0, 5);
If you're dealing with an IEnumerable<T>, you could do the following to get the index of the item matching 4:
IEnumerable<int> values = new[] { 1, 2, 3, 4, 5 };
int indexOf4 = (
values.Select((v, i) => new {v, i})
.FirstOrDefault(vi => vi.v == 4) ?? new {v = 0, i = -1}).i;
This copes with the case where the value source doesn't contain a match (returning -1).
Of course, if you don't mind converting your IEnumerable<T> to a list then you can just call IndexOf:
int indexOf4a = values.ToList().IndexOf(4);
But, I suspect what the question is really looking for is a way to find all the indices for values that match a particular predicate. For example:
IEnumerable<int> big = values.Select((v, i) => new {v, i})
.Where(vi => vi.v > 3)
.Select (vi => vi.i);
which returns the indices of the values > 3: [3, 4].
If the predicate doesn't match any values then you'll get an empty enumerable as the result.
IEnumerable<int> seq = new[] { 1, 2, 4, 5, 3 };
// The indexes of all elements.
var indexes = Enumerable.Range(0, seq.Count());
// The index of the left-most element with value 4.
// NOTE: Will return seq.Count() if the element doesn't exist.
var index = seq.TakeWhile(x => x != 4).Count();
// The indexes of all the elements with value 4.
// NOTE: Be careful to enumerate only once.
int current_index = 0;
var all_indexes =
from e in (
from x in seq
select new { x, Index = current_index++ }
)
where e.x == 4
select e.Index;
You can do it like this:
public static IEnumerable<int> WhereIndices<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
return source.Select(Tuple.Create<T, int>)
.Where(z => predicate(z.Item1)).Select(z => z.Item2);
}
It's an extension method, so put it in a static non-nested class. Use it just like you use Where, that is:
.WhereIndices(num => num == 4)
This should do it. Not sure how efficient it is though..
List<int> list = new List<int>()
{
1,
2,
3,
4,
5
};
var indexes = list.Select(item => list.IndexOf(item));
var index = list.Where(item => item == 4).Select(item => list.IndexOf(item));

Categories

Resources