Assuming we have multiple IEnumerables of the same item count
var multipleEnumerables = new[]
{
new[] { 10, 20, 30 }
new[] { 11, 21, 31 }
new[] { 12, 22, 32 }
new[] { 13, 23, 33 }
}
How can i get the average of those using LINQ which will yield the following result:
new[] { 11.5, 21.5, 31.5 }
The number of enumerables may vary (i.e. not fixed to four).
The number of items in each enumerable may vary (i.e. not fixed to three) but will be the same for all the enumerables for that particular instance.
Update in response to edit
If you have IEnumerable<IEnumerable<Item>> you could use something like this:
IEnumerable<IEnumerable<Test>> multipleEnumerables = new[] {
new [] { new Test { Value = 10 }, new Test { Value = 20 } },
new [] { new Test { Value = 11 }, new Test { Value = 21 } }
};
var averages = multipleEnumerables
.SelectMany(e => e.Select((v, i) => new { Index = i, Item = v }))
.GroupBy(e => e.Index)
.Select(e => e.Select(v => v.Item.Value).Average())
.ToArray();
If you have a simple IEnumerable<IEnumerable<int>>, change the .Select line to: .Select(e => e.Select(v => v.Item).Average()) (but based on your comment, I think that isn't the case).
Use .SelectMany to flatten the list into objects containing the item, and its sub-array index (so 10 and 11 will have Index 0, etc.).
Group by the index.
Use .Select to select the average from each grouping.
Try it online
Try it online with question data
Original answer
You can use .Zip to combine the two lists and compute an average:
var averages = items1
.Zip(items2, (a, b) => (a+b)/2.0)
.ToArray();
Try it online
Due to your interest in zipping more than two IEnumerable<T>s, here's an extension method (ZipAll) that shows how to zip multiple IEnumerable<T> together. Effectively, this rotates the data such that columns become rows.
public static class ZipEx
{
public static IEnumerable<IEnumerable<T>> ZipAll<T>(
this IEnumerable<IEnumerable<T>> src)
{
return src
.Aggregate(
(IEnumerable<IEnumerable<T>>)null,
(acc, curr) => acc == null
? curr.Select(x => x.ToEnumerable())
: acc.Zip(curr, (a, c) => a.Append(c)));
}
public static IEnumerable<T> ToEnumerable<T>(this T item)
{
yield return item;
}
}
Using this ZipAll method, it's now easy to:
var averages = multipleEnumerables.ZipAll().Select(x => x.Average()); //.ToArray()
Related
I have arrays like these :
var name = new[] { "matin", "mobin", "asghar" };
var family = new[] { "shomaxe", "jjj", "reyhane" };
var number = new[] { 1, 2, 3 };
var age = new[] { 21, 23, 24 };
and I want to output them like this :
matin shomaxe 1 21
mobin jjj 2 23
asghar reyhane 3 24
how can I do that with a foreach loop?
and how can I do it with a for loop?
Yet another strange solution in fluent style with .Zip() operator:
var names = new[] { "matin", "mobin", "asghar" };
var families = new[] { "shomaxe", "jjj", "reyhane" };
var numbers = new[] { 1, 2, 3 };
var ages = new[] { 21, 23, 24 };
names
.Zip(families, (name, family) => $"{name} {family}")
.Zip(numbers, (res, number) => $"{res} {number}")
.Zip(ages, (res, age) => $"{res} {age}")
.ToList()
.ForEach(x => Console.WriteLine(x));
From the docs about Zip operator.
Considering your arrays are of equal length as you posted then below should work:
ForEach loop will need a counter variable to keep track of the position you are in the array as opposed to the For loop
ForEach loop
int counter = 0;
foreach(var item in name)
{
Console.WriteLine($"{item}, {family[counter]}, {number[counter]}, {age[counter]}");
counter++;
}
For loop
for (int i = 0; i < name.Length; i++)
{
Console.WriteLine($"{name[i]}, {family[i]}, {number[i]}, {age[i]}");
}
If you want to avoid using an index you can use Zip from System.Linq to combine the lists together. Example below shows combining them into a Tuple (available from C# 7.0).
IEnumerable<(string a, string b, int c, int d)> iterable = name
.Zip(family, (a, b) => (a, b))
.Zip(number, (others, c) => (others.a, others.b, c))
.Zip(age, (others, d) => (others.a, others.b, others.c, d));
foreach(var i in iterable)
{
Console.WriteLine(i);
}
// Outputs:
// (matin, shomaxe, 1, 21)
// (mobin, jjj, 2, 23)
// (asghar, reyhane, 3, 24)
Supposing all of your arrays are of equal size :
for(int i=0;i<name.Length;i++){
Console.WriteLine("{0} {1} {2} {3}",name[i],family[i],number[i],age[i])
}
I don't see how you can do this with a foreach however...
How can I get the first indexes from my list of lists and get it's average value. Currently I have return value on my list:
[["1.11, 2.11, 3.11"], ["2.11, 3.11, 4.11"], ["4.11, 5.11, 6.11"]]
Here is my expected result:
var index0 = 2.44
var index1 = 3.44
var index2 = 4.44
On single list only I am using this to get the average:
var avg = myList.Select(double.Parse).Average();
Any suggestion/comments TIA.
Edit Solution because you edited.
String[][] TValue = new String[][]
{
new String[] {"1.11", "2.11", "3.11" },
new String[] {"2.11", "3.11", "4.11" },
new String[] {"4.11", "5.11", "6.11" }
};
Console.WriteLine("Avg[0] => {0:F2}", TValue.Select(x => double.Parse(x[0])).Average());
Console.WriteLine("Avg[1] => {0:F2}", TValue.Select(x => double.Parse(x[1])).Average());
Console.WriteLine("Avg[2] => {0:F2}", TValue.Select(x => double.Parse(x[2])).Average());
this is what you expected.
hope this work.
It seems that you need to get avg based on columns index instead of rows then .Zip will be one option for you,
Suppose your list of list of string is,
var myList = new List<List<string>>
{
new List<string> { "1.11, 2.11, 3.11" }, //<= Notice here single item in list with comma(,) separated
new List<string> { "2.11, 3.11, 4.11" },
new List<string> { "4.11, 5.11, 6.11" }
};
So you need to first split your string in inner list with comma(,) to get each item as separate string,
var list = myList
.SelectMany(x => x
.Select(y => y.Split(',')
.Select(z => z.Trim())
.ToList()))
.ToList();
Then you can make .Zip on all above 3 list by
var results = list[0]
.Zip(list[1], (a, b) => double.Parse(a) + double.Parse(b))
.Zip(list[2], (x, y) => (x + double.Parse(y)) / 3)
.ToList();
//------------------Print the result---------------
foreach (var item in results)
{
Console.WriteLine(Math.Round(item, 2));
}
How .Zip works?
Lets take all columns value on index 0.
a = "1.11"
b = "2.11"
Then 1st Zip result will be =>
double.Parse(a) + double.Parse(b)
= 1.11 + 2.11
= 3.22
So for 2nd .Zip the x now be the result of the above 1st .Zip that means
x = 3.22
y = "4.11"
Then 2nd Zip result will be =>
(x + double.Parse(y)) / 3
= (3.22 + 4.11) / 3
= 2.44
So for Average of your values at column index 0 => 2.44
In above,
list[0] : 1st list in your list of list of string.
list[1] : 2nd list in your list of list of string.
list[2] : 3rd list in your list of list of string.
Output: (From Console)
You can zip all three lists
using zipThree from How to combine more than two generic lists in C# Zip?
using System;
using System.Collections.Generic;
using System.Linq;
public static class MyFunkyExtensions
{
public static IEnumerable<TResult> ZipThree<T1, T2, T3, TResult>(
this IEnumerable<T1> source,
IEnumerable<T2> second,
IEnumerable<T3> third,
Func<T1, T2, T3, TResult> func)
{
using (var e1 = source.GetEnumerator())
using (var e2 = second.GetEnumerator())
using (var e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
yield return func(e1.Current, e2.Current, e3.Current);
}
}
}
class MainClass {
public static void Main (string[] args) {
var list = new List<List<double>> { new List<double> {1.11,2.11,3.11}, new List<double> {2.11,3.11,4.11}, new List<double> {4.11,5.11,6.11} };
var a = list[0].ZipThree(list[1], list[2], (x, y, z) => (x + y + z) / 3);
Console.WriteLine(
string.Join(",",
a.Select(s => s.ToString())));
}
}
And it returns
2.44333, 3.443333, 4.44333
I'm assuming all the inner lists have the same length, and you want to count the average of the corresponding indices (i.e. index0 is average of 0th value from each list, index1 is average of the 1st value etc.).
In such case to obtain the averages for each index you use the following code:
int listLength = myList.First().Length; // Length for an array
// Count for a List<T>
var averages = Enumerable.Range(0, listLength).Select(
index => myList.Average(innerList => double.Parse(innerList[index]))
).ToList();
You could do so using Linq.
Updated based on comment
var list = new [] { "1.11, 2.11, 3.11" , "2.11,3.11, 4.11" , "4.11,5.11,6.11" };
var collection = list.Select(x => x.Split(',').Select(c => double.Parse(c)).ToList()).ToList();
var result = collection.First()
.Select((dummy, i) =>
collection.Average(inner => inner[i]));
Output
2.44
3.44
4.44
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
Say I have the following data
IEnumerable<IEnumerable<int>> items = new IEnumerable<int>[] {
new int[] { 1, 2, 3, 4 },
new int[] { 5, 6 },
new int[] { 7, 8, 9 }
};
What would be the easiest way to return a flat list with the items interleaved so I'd get the result:
1, 5, 7, 2, 6, 8, 3, 9, 4
Note: The number of inner lists is not known at runtime.
What you're describing is essentially a Transpose Method where overhanging items are included and the result is flattened. Here's my attempt:
static IEnumerable<IEnumerable<T>> TransposeOverhanging<T>(
this IEnumerable<IEnumerable<T>> source)
{
var enumerators = source.Select(e => e.GetEnumerator()).ToArray();
try
{
T[] g;
do
{
yield return g = enumerators
.Where(e => e.MoveNext()).Select(e => e.Current).ToArray();
}
while (g.Any());
}
finally
{
Array.ForEach(enumerators, e => e.Dispose());
}
}
Example:
var result = items.TransposeOverhanging().SelectMany(g => g).ToList();
// result == { 1, 5, 7, 2, 6, 8, 3, 9, 4 }
The solution below is very straight forward. As it turns out, it is also nearly twice as fast as the solution proposed by dtb.
private static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source )
{
var queues = source.Select(x => new Queue<T>(x)).ToList();
while (queues.Any(x => x.Any())) {
foreach (var queue in queues.Where(x => x.Any())) {
yield return queue.Dequeue();
}
}
}
Here's my attempt, based on dtb's answer. It avoids the external SelectMany and internal ToArray calls.
public static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source)
{
var enumerators = source.Select(e => e.GetEnumerator()).ToArray();
try
{
bool itemsRemaining;
do
{
itemsRemaining = false;
foreach (var item in
enumerators.Where(e => e.MoveNext()).Select(e => e.Current))
{
yield return item;
itemsRemaining = true;
}
}
while (itemsRemaining);
}
finally
{
Array.ForEach(enumerators, e => e.Dispose());
}
}
Disposed all enumerators, even when exceptions are thrown
Evaluates the outer sequence eagerly, but uses lazy evaluation for the inner sequences.
public static IEnumerable<T> Interleave<T>(IEnumerable<IEnumerable<T>> sequences)
{
var enumerators = new List<IEnumerator<T>>();
try
{
// using foreach here ensures that `enumerators` contains all already obtained enumerators, in case of an expection is thrown here.
// this ensures proper disposing in the end
foreach(var enumerable in sequences)
{
enumerators.Add(enumerable.GetEnumerator());
}
var queue = new Queue<IEnumerator<T>>(enumerators);
while (queue.Any())
{
var enumerator = queue.Dequeue();
if (enumerator.MoveNext())
{
queue.Enqueue(enumerator);
yield return enumerator.Current;
}
}
}
finally
{
foreach(var enumerator in enumerators)
{
enumerator.Dispose();
}
}
}
Though its not as elegant as "dtb"'s answer, but it also works and its a single liner :)
Enumerable.Range(0, items.Max(x => x.Count()))
.ToList()
.ForEach(x =>
{
items
.Where(lstChosen => lstChosen.Count()-1 >= x)
.Select(lstElm => lstElm.ElementAt(x))
.ToList().ForEach(z => Console.WriteLine(z));
});
What's the most efficient way to write a method that will compare n lists and return all the values that do not appear in all lists, so that
var lists = new List<List<int>> {
new List<int> { 1, 2, 3, 4 },
new List<int> { 2, 3, 4, 5, 8 },
new List<int> { 2, 3, 4, 5, 9, 9 },
new List<int> { 2, 3, 3, 4, 9, 10 }
};
public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists)
{
//...fast algorithm here
}
so that
lists.GetNonShared();
returns 1, 5, 8, 9, 10
I had
public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists)
{
return list.SelectMany(item => item)
.Except(lists.Aggregate((a, b) => a.Intersect(b));
}
But I wasn't sure if that was efficient. Order does not matter. Thanks!
public static IEnumerable<T> GetNonShared<T>(this IEnumerable<IEnumerable<T>> list)
{
return list.SelectMany(x => x.Distinct()).GroupBy(x => x).Where(g => g.Count() < list.Count()).Select(group => group.Key);
}
EDIT: I think I'd think of it like this...
You want the union of all the lists, minus the intersection of all the lists. That's effectively what your original does, leaving Except to do the "set" operation of Union despite getting duplicate inputs. In this case I suspect you could do this more efficiently just building up two HashSets and doing all the work in-place:
public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists)
{
using (var iterator = lists.GetEnumerator())
{
if (!iterator.MoveNext())
{
return new T[0]; // Empty
}
HashSet<T> union = new HashSet<T>(iterator.Current.ToList());
HashSet<T> intersection = new HashSet<T>(union);
while (iterator.MoveNext())
{
// This avoids iterating over it twice; it may not be necessary,
// it depends on how you use it.
List<T> list = iterator.Current.Toist();
union.UnionWith(list);
intersection = intersection.IntersectWith(list);
}
union.ExceptWith(intersection);
return union;
}
}
Note that this is now eager, not deferred.
Here's an alternative option:
public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists)
{
return list.SelectMany(list => list)
.GroupBy(x => x)
.Where(group => group.Count() < lists.Count)
.Select(group => group.Key);
}
If it's possible for a list to contain the same item more than once, you'd want a Distinct call in there:
public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists)
{
return list.SelectMany(list => list.Distinct())
.GroupBy(x => x)
.Where(group => group.Count() < list.Count)
.Select(group => group.Key);
}
EDIT: Now I've corrected this, I understand your original code... and I suspect I can find something better... thinking...
I think you need to create an intermediate step, which is finding all the items which are common to all lists. This is easy to do with set logic - it's just the set of items in the first list intersected with the set of items in each succeeding list. I don't think that step's doable in LINQ, though.
class Program
{
static void Main(string[] args)
{
IEnumerable<IEnumerable<int>> lists = new List<IEnumerable<int>> {
new List<int> { 1, 2, 3, 4 },
new List<int> { 2, 3, 4, 5, 8 },
new List<int> { 2, 3, 4, 5, 9, 9 },
new List<int> { 2, 3, 3, 4, 9, 10 }
};
Console.WriteLine(string.Join(", ", GetNonShared(lists)
.Distinct()
.OrderBy(x => x)
.Select(x => x.ToString())
.ToArray()));
Console.ReadKey();
}
public static HashSet<T> GetShared<T>(IEnumerable<IEnumerable<T>> lists)
{
HashSet<T> result = null;
foreach (IEnumerable<T> list in lists)
{
result = (result == null)
? new HashSet<T>(list)
: new HashSet<T>(result.Intersect(list));
}
return result;
}
public static IEnumerable<T> GetNonShared<T>(IEnumerable<IEnumerable<T>> lists)
{
HashSet<T> shared = GetShared(lists);
return lists.SelectMany(x => x).Where(x => !shared.Contains(x));
}
}
public static IEnumerable<T> GetNonShared<T>(this IEnumerable<IEnumerable<T>> list)
{
var lstCnt=list.Count(); //get the total number if items in the list
return list.SelectMany (l => l.Distinct())
.GroupBy (l => l)
.Select (l => new{n=l.Key, c=l.Count()})
.Where (l => l.c<lstCnt)
.Select (l => l.n)
.OrderBy (l => l) //can be commented
;
}
//use HashSet and SymmetricExceptWith for .net >= 4.5