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
Related
Suppose I have a list of strings [city01, city01002, state02, state03, city04, statebg, countryqw, countrypo]
How do I group them in a dictionary of <string, List<Strings>> like
city - [city01, city04, city01002]
state- [state02, state03, statebg]
country - [countrywq, countrypo]
If not code, can anyone please help with how to approach or proceed?
As shown in other answers you can use the GroupBy method from LINQ to create this grouping based on any condition you want. Before you can group your strings you need to know the conditions for how a string is grouped. It could be that it starts with one of a set of predefined prefixes, grouped by whats before the first digit or any random condition you can describe with code. In my code example the groupBy method calls another method for every string in your list and in that method you can place the code you need to group the strings as you want by returning the key to group the given string under. You can test this example online with dotnetfiddle: https://dotnetfiddle.net/UHNXvZ
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<string> ungroupedList = new List<string>() {"city01", "city01002", "state02", "state03", "city04", "statebg", "countryqw", "countrypo", "theFirstTown"};
var groupedStrings = ungroupedList.GroupBy(x => groupingCondition(x));
foreach (var a in groupedStrings) {
Console.WriteLine("key: " + a.Key);
foreach (var b in a) {
Console.WriteLine("value: " + b);
}
}
}
public static string groupingCondition(String s) {
if(s.StartsWith("city") || s.EndsWith("Town"))
return "city";
if(s.StartsWith("country"))
return "country";
if(s.StartsWith("state"))
return "state";
return "unknown";
}
}
You can use LINQ:
var input = new List<string>()
{ "city01", "city01002", "state02",
"state03", "city04", "statebg", "countryqw", "countrypo" };
var output = input.GroupBy(c => string.Join("", c.TakeWhile(d => !char.IsDigit(d))
.Take(4))).ToDictionary(c => c.Key, c => c.ToList());
i suppose you have a list of references you are searching in the list:
var list = new List<string>()
{ "city01", "city01002", "state02",
"state03", "city04", "statebg", "countryqw", "countrypo" };
var tofound = new List<string>() { "city", "state", "country" }; //references to found
var result = new Dictionary<string, List<string>>();
foreach (var f in tofound)
{
result.Add(f, list.FindAll(x => x.StartsWith(f)));
}
In the result, you have the dictionary wanted. If no value are founded for a reference key, the value of key is null
Warning: This answer has a combinatorial expansion and will fail if your original string set is large. For 65 words I gave up after running for a couple of hours.
Using some IEnumerable extension methods to find Distinct sets and to find all possible combinations of sets, you can generate a group of prefixes and then group the original strings by these.
public static class IEnumerableExt {
public static bool IsDistinct<T>(this IEnumerable<T> items) {
var hs = new HashSet<T>();
foreach (var item in items)
if (!hs.Add(item))
return false;
return true;
}
public static bool IsEmpty<T>(this IEnumerable<T> items) => !items.Any();
public static IEnumerable<IEnumerable<T>> AllCombinations<T>(this IEnumerable<T> start) {
IEnumerable<IEnumerable<T>> HelperCombinations(IEnumerable<T> items) {
if (items.IsEmpty())
yield return items;
else {
var head = items.First();
var tail = items.Skip(1);
foreach (var sequence in HelperCombinations(tail)) {
yield return sequence; // Without first
yield return sequence.Prepend(head);
}
}
}
return HelperCombinations(start).Skip(1); // don't return the empty set
}
}
var keys = Enumerable.Range(0, src.Count - 1)
.SelectMany(n1 => Enumerable.Range(n1 + 1, src.Count - n1 - 1).Select(n2 => new { n1, n2 }))
.Select(n1n2 => new { s1 = src[n1n2.n1], s2 = src[n1n2.n2], Dist = src[n1n2.n1].TakeWhile((ch, n) => n < src[n1n2.n2].Length && ch == src[n1n2.n2][n]).Count() })
.SelectMany(s1s2d => new[] { new { s = s1s2d.s1, s1s2d.Dist }, new { s = s1s2d.s2, s1s2d.Dist } })
.Where(sd => sd.Dist > 0)
.GroupBy(sd => sd.s.Substring(0, sd.Dist))
.Select(sdg => sdg.Distinct())
.AllCombinations()
.Where(sdgc => sdgc.Sum(sdg => sdg.Count()) == src.Count)
.Where(sdgc => sdgc.SelectMany(sdg => sdg.Select(sd => sd.s)).IsDistinct())
.OrderByDescending(sdgc => sdgc.Sum(sdg => sdg.First().Dist)).First()
.Select(sdg => sdg.First())
.Select(sd => sd.s.Substring(0, sd.Dist))
.ToList();
var groups = src.GroupBy(s => keys.First(k => s.StartsWith(k)));
I have two string list , first list contains Ids for corresponding second list each element. Definition of the list,
IdsList ["Id1","Id2"]
ShippingsNoList ["n1,n2..","t1,t2"]
means that n1,n2->Id1, t1,t2->Id2
IdsList format -> A-date-B
ShippingNumbersList format-> number1,number2,etc.
My purpose combine two list and return result as string. If I find ShippingNumber which equals another ShippingNumber(s) and their Id's date should also be matched, then I should take Shipping Number and related Ids. One shipping Number may be already assigned more than one Id's which date is same.
Example:
IdsList=["A-28.03.18-B",
"S-17.05.18-G",
"L-17.05.18-P",
"M-28.03.18-T",
"B-17.05.18-U"]
ShippingNumbersList=["100,200,300",
"100,900",
"200,300,100",
"100,900,300",
"100,300"]
Expected Result:
100-> A-28.03.18-B,M-28.03.18-T
300-> A-28.03.18-B,M-28.03.18-T
100-> S-17.05.18-G,L-17.05.18-P,B-17.05.18-U
300-> L-17.05.18-P, B-17.05.18-U
Try this LINQ "beauty".
var idsList = new string[]
{
"A-28.03.18-B",
"S-17.05.18-G",
"L-17.05.18-P",
"M-28.03.18-T",
"B-17.05.18-U"
};
var shippingNumbersList = new string[]
{
"100,200,300",
"100,900",
"200,300,100",
"100,900,300",
"100,300"
};
var data = idsList
.Zip(shippingNumbersList, (x, y) =>
{
//parse the entry of the idsList ('x') in a dateTime
var date = DateTime.Parse(x.Split("-")[1]); //<-- may need to use DateTime.ParseExact(x.Split('-')[1], "dd.MM.yy", CultureInfo.InvariantCulture) - depending on the culture you are using, this will now work on any machine
//parse the entry of the shippingNumbersList ('y') in a IEnumerable<int>
var numbers = y.Split(",").Select(int.Parse);
//make a IEnumerable of the two different data, consisting of (Id, Date, ShippingNumber) <- a single ShippingNumber, thats why we call numbers.Select
return numbers.Select(number => (Id: x, Date: date, ShippingNumber: number));
}) //<-- use ZIP to combine the two lists together
.SelectMany(x => x) //<-- use SELECTMANY to get a flat list of each "id" with the x number of "shippingNumberList"
.GroupBy(x => (Date: x.Date, ShippingNumber: x.ShippingNumber)) //<-- use GROUPBY for the Date and ShippingNumber
.Where(x => x.Count() > 1) //<-- use WHERE to filter those who only have 1 entry in a group consisting of Date+ShippingNumber
.Select(x => x.Key.ShippingNumber + "-> " + string.Join(",", x.Select(y => y.Id))) //<-- use SELECT to resolve the group to a string, there the Key is the combined Date + ShippingNumber and the Value is the flatList of that group
.ToList(); //<-- use TOLIST to make a List out of the IEnumerable
Had to fix some stuff for it to run on dotnetfiddle, but here you go:
https://dotnetfiddle.net/bKpUDz
Here is another solution which is tested :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
using System.Text.RegularExpressions;
namespace ConsoleApplication100
{
class Program
{
static void Main(string[] args)
{
List<string> IdsList = new List<string>() {
"A-28.03.18-B",
"S-17.05.18-G",
"L-17.05.18-P",
"M-28.03.18-T",
"B-17.05.18-U"
};
List<string> ShippingNumbersList = new List<string>() {
"100,200,300",
"100,900",
"200,300,100",
"100,900,300",
"100,300"
};
var results = Shipping.MergeList(IdsList, ShippingNumbersList);
}
}
public class Shipping
{
public static object MergeList(List<string> ids, List<string> numbers)
{
string pattern = #"\w-(?'day'[^\.]+)\.(?'month'[^\.]+)\.(?'year'[^-]+)";
List<KeyValuePair<DateTime, string>> idDates = new List<KeyValuePair<DateTime,string>>();
foreach(string id in ids)
{
Match match = Regex.Match(id,pattern);
idDates.Add(new KeyValuePair<DateTime, string>( new DateTime(2000 + int.Parse(match.Groups["year"].Value), int.Parse(match.Groups["month"].Value), int.Parse(match.Groups["day"].Value)), id));
}
var groups = idDates.SelectMany((x, i) => numbers[i].Split(new char[] {','}).Select(y => new { idDate = x, number = y })).ToList();
var groupDates = groups.GroupBy(x => new { date = x.idDate.Key, number = x.number }).ToList();
var results = groupDates.Select(x => new { number = x.Key.number, ids = x.Select(y => y.idDate.Value).ToList() }).ToList();
return results;
}
}
}
Data :
var IdsList =new string[] {"A-28.03.18-B",
"S-17.05.18-G",
"L-17.05.18-P",
"M-28.03.18-T",
"B-17.05.18-U" };
var ShippingNumbersList =new string[] {"100,200,300",
"100,900",
"200,300,100",
"100,900,300",
"100,300" };
Making resuts:
//normalizing data and make a list of joined columns
var normalizedlist = IdsList
.Select((Ids, index) => new { Ids = Ids, ShippingNumbers = ShippingNumbersList[index].Split(',') })
.ToList();
//for each distinct ShippingNumber find and write respective Id
foreach (var ShippingNumber in normalizedlist.SelectMany(x=>x.ShippingNumbers).Distinct())
{
//fitering and then grouping by date
var filtered = normalizedlist.Where(y => y.ShippingNumbers.Contains(ShippingNumber))
.GroupBy(y => y.Ids.Split('-')[1])
.Where(y => y.Count() > 1)
.Select(y => y.Select(z=>z.Ids));
foreach (var date in filtered)
{
Console.WriteLine($"{ShippingNumber}>>{string.Join(",",date.ToArray())}");
}
}
Output:
100>>A-28.03.18-B,M-28.03.18-T
100>>S-17.05.18-G,L-17.05.18-P,B-17.05.18-U
300>>A-28.03.18-B,M-28.03.18-T
300>>L-17.05.18-P,B-17.05.18-U
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()
I’m trying to convert from this answer the code:
static IEnumerable<IEnumerable<T>> GetKCombs<T>(IEnumerable<T> list, int length) where T : IComparable
{
if (length == 1) return list.Select(t => new T[] { t });
return GetKCombs(list, length - 1)
.SelectMany(t => list.Where(o => o.CompareTo(t.Last()) > 0),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
Into a list of strings. For example I want this output {1,2} {1,3} to convert it to "1,2","1,3" (this is 2 seperate string) but I cant get it. I cant even understand how I can read the results of the above function. this is my code:
int[] numbers = ListEditText.Text.Split(',').Select(x => int.Parse(x)).ToArray();
var combinations = GetKCombs(numbers, 2);
stringCombinations = combinations.Select(j => j.ToString()).Aggregate((x, y) => x + "," + y);
In the end all the results i will add them on a List with all the possible unique combinations
For example for the numbers {1,2,3} i want this List:
'1','2','3','1,2','1,3','2,3','1,2,3'
This is my code right now:
List<string> stringCombinations = new List<string>();
for (int i = 0; i < numbers.Count(); i++)
{
combinations = GetKCombs(numbers, i + 1).Select(c => string.Join(",", c));
stringCombinations.AddRange(combinations);
}
You can try first joining the results of the inner IEnumerables
var combinations = GetKCombs(numbers, 2).Select(c => string.Join(",", c));
and then concatenating them into a single string
var combinationString = string.Join("; ", combinations); // "1,2; 1,3"
Based on your edits -- if I got you right -- you can try doing
var combinationStrings =
numbers
.SelectMany((_, i) =>
GetKCombs(numbers, i + 1) // get combinations for each 'length'
.Select(c => string.Join(",", c))) // join them to a string
.ToList();
Try
var stringCombinations = string.Join(",", combinations.Select(j => $#"""{string.Join(",", j)}"""));
It prints exactly the output you want.
I have a troublesome query to write. I'm currently writing some nasty for loops to solve it, but I'm curious to know if Linq can do it for me.
I have:
struct TheStruct
{
public DateTime date {get; set;} //(time portion will always be 12 am)
public decimal A {get; set;}
public decimal B {get; set;}
}
and a list that contains these structs. Let's say it's ordered this way:
List<TheStruct> orderedList = unorderedList.OrderBy(x => x.date).ToList();
If you put the orderedList struct dates in a set they will always be contiguous with respect to the day.. that is if the latest date in the list was 2011/01/31, and the earliest date in the list was 2011/01/01, then you'd find that the list would contain 31 items, one for each date in January.
Ok, so what I want to do is group the list items such that:
Each item in a group must contain the same Decimal A value and the same Decimal B value
The date values in a group must form a set of contiguous dates, if the date values were in order
If you summed up the sums of items in each group, the total would equal the number of items in the original list (or you could say a struct with a particular date can't belong to more than one group)
Any Linq masters know how to do this one?
Thanks!
You can group adjacent items in a sequence using the GroupAdjacent Extension Method (see below):
var result = unorderedList
.OrderBy(x => x.date)
.GroupAdjacent((g, x) => x.A == g.Last().A &&
x.B == g.Last().B &&
x.date == g.Last().date.AddDays(1))
.ToList();
Example:
(1,1) 2011-01-01 \
(1,1) 2011-01-02 > Group 1
(1,1) 2011-01-03 __/
(2,1) 2011-01-04 \
(2,1) 2011-01-05 > Group 2
(2,1) 2011-01-06 __/
(1,1) 2011-01-07 \
(1,1) 2011-01-08 > Group 3
(1,1) 2011-01-09 __/
(1,1) 2011-02-01 \
(1,1) 2011-02-02 > Group 4
(1,1) 2011-02-03 __/
Extension Method:
static IEnumerable<IEnumerable<T>> GroupAdjacent<T>(
this IEnumerable<T> source, Func<IEnumerable<T>, T, bool> adjacent)
{
var g = new List<T>();
foreach (var x in source)
{
if (g.Count != 0 && !adjacent(g, x))
{
yield return g;
g = new List<T>();
}
g.Add(x);
}
yield return g;
}
Here is an entry for "Most Convoluted way to do this":
public static class StructOrganizer
{
public static IEnumerable<Tuple<Decimal, Decimal, IEnumerable<MyStruct>>> OrganizeWithoutGaps(this IEnumerable<MyStruct> someStructs)
{
var someStructsAsList = someStructs.ToList();
var lastValuesSeen = new Tuple<Decimal, Decimal>(someStructsAsList[0].A, someStructsAsList[0].B);
var currentList = new List<MyStruct>();
return Enumerable
.Range(0, someStructsAsList.Count)
.ToList()
.Select(i =>
{
var current = someStructsAsList[i];
if (lastValuesSeen.Equals(new Tuple<Decimal, Decimal>(current.A, current.B)))
currentList.Add(current);
else
{
lastValuesSeen = new Tuple<decimal, decimal>(current.A, current.B);
var oldList = currentList;
currentList = new List<MyStruct>(new [] { current });
return new Tuple<decimal, decimal, IEnumerable<MyStruct>>(lastValuesSeen.Item1, lastValuesSeen.Item2, oldList);
}
return null;
})
.Where(i => i != null);
}
// To Test:
public static void Test()
{
var r = new Random();
var sampleData = Enumerable.Range(1, 31).Select(i => new MyStruct {A = r.Next(0, 2), B = r.Next(0, 2), date = new DateTime(2011, 12, i)}).OrderBy(s => s.date).ToList();
var sortedData = sampleData.OrganizeWithoutGaps();
Console.Out.WriteLine("Sample Data:");
sampleData.ForEach(s => Console.Out.WriteLine("{0} = ({1}, {2})", s.date, s.A, s.B));
Console.Out.WriteLine("Output:");
sortedData.ToList().ForEach(s => Console.Out.WriteLine("({0}, {1}) = {2}", s.Item1, s.Item2, String.Join(", ", s.Item3.Select(st => st.date))));
}
}
If I understood you well, a simple Group By would do the trick:
var orderedList = unorderedList.OrderBy(o => o.date).GroupBy(s => new {s.A, s.B});
Just that. To print the results:
foreach (var o in orderedList) {
Console.WriteLine("Dates of group {0},{1}:", o.Key.A, o.Key.B);
foreach(var s in o){
Console.WriteLine("\t{0}", s.date);
}
}
The output would be like:
Dates of group 2,3:
02/12/2011
03/12/2011
Dates of group 4,3:
03/12/2011
Dates of group 1,2:
04/12/2011
05/12/2011
06/12/2011
Hope this helps.
Cheers