I have multiple lists of strings, IList<string>, that I want to consolidate in one list showing distinct string and count for each item (like a dictionary). what is the most efficient way to do this?
LINQ (certainly the most efficient in terms of code to type and maintain; the overall performance will be about the same as any other approach):
if the lists are in separate variables:
var qry = from s in listA.Concat(listB).Concat(listC) // etc
group s by s into tmp
select new { Item = tmp.Key, Count = tmp.Count() };
if the lists are all in a parent list (of lists):
var qry = from list in lists
from s in list
group s by s into tmp
select new { Item = tmp.Key, Count = tmp.Count() };
Then if you really want a list:
var resultList = qry.ToList();
Dictionary<string, int> count = new Dictionary<string, int>();
foreach(IList<int> list in lists)
foreach(int item in list) {
int value;
if (count.TryGetValue(item, out value))
count[item] = value + 1;
else
count[item] = 1;
}
List<List<string>> source = GetLists();
//
Dictionary<string, int> result = source
.SelectMany(sublist => sublist)
.GroupBy(s => s)
.ToDictionary(g => g.Key, g => g.Count())
Related
want to check if list contains same items
var listme = new List<string>();
listme.Add("list1");
listme.Add("list1");
And want to count the number of same items and copy it and then remove it from list.
You can do it in a single LINQ statement with GroupBy and ToDictionary:
var dupCounts = listme
.GroupBy(s => s)
.Where(g => g.Count() > 1) // Keep only groups with duplicates
.ToDictionary(g => g.Key, g => g.Count());
This produces a Dictionary<string,int> where each item from the list is mapped to its corresponding count from the original list. Now you can remove all duplicates from the original list:
listme.RemoveAll(dupCounts.Keys);
Try to use HashTable instead of List
Hashtable hashtable = new Hashtable();
hashtable[1] = "One";
hashtable[2] = "Two";
hashtable[13] = "Thirteen"
You can use linq, see below:
public static void Main()
{
var listme = new List<string> {"A", "A", "B", "C", "C"};
// count
var countDict = listme.GroupBy(i => i)
.ToDictionary(i => i.Key, i => i.Count());
foreach (var kv in countDict)
{
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
// remove
listme.RemoveAll(s => s == "A");
foreach (string s in listme)
{
Console.WriteLine(s);
}
Console.ReadLine();
}
I have a List looks like:
List<int> List1= new List<int>(){3,4,5};
and another looks like:
List<int> List2 = new List<int>(){1,2,3,4,5,6};
How can I use Linq to get an array of all of the indices of List1 from List2 like below:
var ResultList = {2,3,4};
var ResultList = List1.Select(x => List2.IndexOf(x));
This is a longer solution but prevents a nested loop through the array which may be faster if the arrays are huge (but slower if the arrays are small).
List<int> List1= new List<int>(){3,4,5};
List<int> List2 = new List<int>(){1,2,3,4,5,6};
var lookup = new Dictionary<int, int>();
for(var i=0; i<List2.Count; i++) {
lookup[List2[i]] = i;
}
List<int> Result = List1.Select(i => {
int index;
return lookup.TryGetValue(i, out index) ? index : -1;
}).ToList();
You can also do the overloaded version of Select statement to select the Value and return the Index:
var result = List2.Select((a, b) => new {Value = a, Index = b})
.Where(x => List1.Any(d => d == x.Value))
.Select(c => c.Index).ToArray();
If your List2 contains more than one instance of a List1 value (or Equality) type, then you can use the indexed overload of Select to find all the duplicates:
var List1= new List<int>(){3,4,5};
var List2 = new List<int>(){1,2,3,4,5,6,1,2,3,5};
var result = List2.Select((x, idx) => Tuple.Create(x, idx))
.Where(t => List1.Contains(t.Item1))
.Select(x => x.Item2)
// 2,3,4,8,9
or better, using C#7 Value Tuples
List2.Select((x, idx) => (X:x, Idx:idx))
.Where(t => List1.Contains(t.X))
.Select(x => x.Idx);
(.IndexOf returns just the first index found in the target)
I have a list of a list of strings:
List<List<String>> pChain;
It might have repeated lists of strings (two list of strings are equal if they have the same strings in the same order). I want to have the count of each distinct list in the main list. I tried:
var results = (from t in pChain
group t by new { t }
into g
select new
{
g.Key,
Count = g.Count(),
}).OrderByDescending(x => x.Count).ToList();
foreach (var v in results)
{
ListViewItem lv = listView2.Items.Add(v.Key.ToString());
lv.SubItems.Add(v.Count + "");
}
But it doesn't group similar list of strings into one list and doesn't count them.
You can use SelectMany + Distinct:
var allDistinctItems = pChain.SelectMany(list => list).Distinct();
If you want the count use int countOfDistinctItems = allDistinctItems.Count();.
If you want a dictionary you could use:
Dictionary<string, int> itemCounts = pChain.SelectMany(list => list)
.GroupBy(item => item)
.ToDictionary(g => g.Key, g => g.Count());
You can check if a list of lists contains an specific list by iterating through its elements and checking if they are SequenceEqual(). You should be able to remove the duplicate lists with this:
for(int i = 0; i < pChain.Count(); i++)
{
// If the amount(Count) of SequenceEqual lists in pChain for the current iteration
// of pChain (pChain[i]) is > 1
if (pChain.Count(l => l.SequenceEqual(pChain[i])) > 1)
pChain.RemoveAt(i);
}
Thus the amount of distinct lists would be:
int count = pChain.Count();
You can put the code above into a single linQ line this way:
pChain.Select((x, y) => new { list = x, Index = y }).ToList()
.ForEach(l1 => {
if (pChain.Count(l2 => l2.SequenceEqual(l1.list)) > 1)
pChain.RemoveAt(l1.Index);
});
I tried Aggregate function to join the strings of the inner list to a string resulted from concatenating them. Then applied the GroupBy to this list.
Dictionary<string, int> itemCounts =
pChain.Select(list => list.Aggregate((i, j) => j + '/' + i))
.GroupBy(item => item).OrderByDescending(x => x.Key)
.ToDictionary(g => g.Key.ToString(), g => g.Count());
foreach (var v in itemCounts)
{
ListViewItem lv = listView2.Items.Add(v.Key.ToString());
lv.SubItems.Add(v.Value + "");
}
Simple (not for me yet I guess) LINQ: I have a List of arrays, and I want to get a collection containing the average of each column. Something like:
var myCollection = new List<double[]>();
myCollection.Add(new []{1,2,3,4,5});
myCollection.Add(new []{3,4,5,6,7});
// Your answer --> {2,3,4,5,6}
Like so?
var myCollection = new List<double[]>();
myCollection.Add(new double[]{1,2,3,4,5});
myCollection.Add(new double[]{3,4,5,6,7});
var qry = (from col in Enumerable.Range(0, myCollection.Min(arr => arr.Length))
select myCollection.Average(arr => arr[col])).ToList();
Original answer from when the question referred to a "2 dimensional array":
How about (note - you may need to reverse col and row depending on how you choose to orient your data):
int[,] data = {{0,1,2},{3,4,5}};
var qry = (from col in Enumerable.Range(0, data.GetLength(0))
select new {
col, avg = Enumerable.Range(0, data.GetLength(1))
.Select(row => data[col, row]).Average()
}).ToList();
foreach(var result in qry) {
Console.WriteLine("{0}: {1}", result.col, result.avg);
}
Or if you just want the averages (not the anon-type):
var qry = (from col in Enumerable.Range(0, data.GetLength(0))
select Enumerable.Range(0, data.GetLength(1))
.Select(row => data[col, row]).Average()).ToList();
Since you have modified the question, changing the 2 dimensional array to List<double[]>, another solution is:
var result = myCollection.Aggregate((arr1, arr2) => arr1.Zip(arr2, (x, y) => (x + y) / 2).ToArray())
I want to use LINQ to convert this
IEnumerable<int>[] value1ByType = new IEnumerable<int>[3];
value1ByType[0]= new [] { 0};
value1ByType[1]= new [] {10,11};
value1ByType[2]= new [] {20};
var value2ToType = new Dictionary<int,int> {
{100,0},
{101,1},
{102,2},
{103,1}};
to this
var value2ToValue1 = new Dictionary<int,int> {
{100, 0},
{101,10},
{102,20},
{103,11}};
Is there a way to do this with LINQ? Without LINQ I would use multiple IEnumerators, one for each IEnumerable of value1ByType. like this:
// create enumerators
var value1TypeEnumerators = new List<IEnumerator<int>>();
for (int i = 0; i < value1ByType.Length; i++)
{
value1TypeEnumerators.Add(value1ByType[i].GetEnumerator());
value1TypeEnumerators[i].MoveNext();
}
// create wanted dictionary
var value2ToValue1 = new Dictionary<int, int>();
foreach (var item in Value2ToType)
{
int value1=value1TypeEnumerators[item.Value].Current;
value2ToValue1.Add(item.Key, value1);
value1TypeEnumerators[item.Value].MoveNext();
}
Any Idea how to do this in LINQ?
Not pure but you can at least do ...
var enumerators = value1ByType.Select(v => v.GetEnumerator()).ToArray();
var value2ToValue1 = value2ToType
.ToDictionary(x => x.Key, x => { enumerators[x.Value].MoveNext(); return enumerators[x.Value].Current; });
But there are so many ways this could go wrong it begs the question - why was the data in those data-structures anyway? and can you fix that instead? How did you end up with exactly the right number of references in the 2nd data structure to elements in the first?
I'm pretty sure that #Hightechrider's solution is most performant than this one, but if you really like the syntax sugar way, you can do it like this:
public IDictionary<int, int> MergeSequences(IEnumerable<int>[] value1ByType, Dictionary<int, int> value2ToType)
{
int pos = 0;
var value1ByTypePos = from byType in value1ByType
select new { Pos = pos++, Enumerator = byType.GetEnumerator() };
return (from byType in value1ByTypePos
join toType in value2ToType
on byType.Pos equals toType.Value
select new { toType.Key, Value = byType.Enumerator.GetNext() })
.ToDictionary(pair => pair.Key, pair => pair.Value);
}
I've added an extension method to the IEnumerator interface like this:
public static T GetNext<T>(this IEnumerator<T> enumerator)
{
if (!enumerator.MoveNext())
throw new InvalidOperationException();
return enumerator.Current;
}
Now you have to be aware that any of this solutions can give you slightly different results, depending on how elements in the dictionary are enumerated. For example, another valid result to this code is:
var value2ToValue1 = new Dictionary<int,int> {
{100, 0},
{103, 10},
{102, 20},
{101, 11}};
Notice that now 101 is paired with 11 and 103 is paired with 10. If this is a problem, then you should use a SortedDictionary<int, int> when defining value2ToType variable.
What you can do for sure is replace the first part with the following:
var value1TypeEnumerators = value1ByType.ToList();
instead of using an enumerator.
If I do not care about performance I could also write:
var value2Ordered = Value2ToType.OrderBy(x => x.Value).Select(x=>x.Key);
var value1Ordered = from item in value1ByType from subitem in item select subitem;
var value2ToValue1 = value2Ordered.Zip(value1Ordered, (x, y) => new { Key = x, Value = y })
.ToDictionary(item => item.Key, item => item.Value);
I used the zip method from a stackoverflow community wiki. I didn't test this with the c#4.0 zip method