I have a very simple List<string> setup which contains lots of single characters per item (IE a foreach would console out to "a" "k" "p" etc)
What I'd like to do is be able to group the items and also count how many of each occurs so I'd get an output similar to:
a - 2
t - 3
y - 3
Any tips on the best way to do this?
I am using .Net 4 if that's any help.
(Given that each entry is a single character, is there any reason you don't have a List<char> by the way?)
How about:
// To get a Dictionary<string, int>
var counts = list.GroupBy(x => x)
.ToDictionary(g => g.Key, g => g.Count());
// To just get a sequence
var counts = list.GroupBy(x => x)
.Select(g => new { Text = g.Key, Count = g.Count() });
Note that this is somewhat inefficient in terms of internal representation. You could definitely do it more efficiently "manually", but it would also take more work. Unless your list is large, I would stick to this.
The easiest way to do this is the Linq using
var list = new[] { "a", "a", "b", "c", "d", "b" };
var grouped = list
.GroupBy(s => s)
.Select(g => new { Symbol = g.Key, Count = g.Count() });
foreach (var item in grouped)
{
var symbol = item.Symbol;
var count = item.Count;
}
var list = new[] {"a", "t", "t", "y", "a", "y", "y", "t"};
var result = (from item in list
group item by item into itemGroup
select String.Format("{0} - {1}", itemGroup.Key, itemGroup.Count()));
Related
So I have a list of items -> A, B, C, D.
C and D are included more than once, and A and B, more than twice. This list can go on and on, so we do not know how many times an item will be included.
I need to create a new list that will have the item in one column and the number of instances of that item in another column, but I do not know how to do this. I may need to use a tuple or a class, but I am not fully sure how to implement either...
What you actually need is to Group the items of your list and perform a group operation, which is Count in your case to calculate how many times does it exist.
This is how you may initialize your list:
List<string> myList = new List<string>() { "A", "B", "C", "D", "A", "B", "C", "D", "A", "B" };
and then you will group it using GroupBy function and apply the Count aggregate function on each group.
myList
.GroupBy(item => item)
.Select(g => new {Key = g.Key, Count = g.Count()})
.ToList();
This will result in the table you need.
You can try like this:
var myList = new List<String>() { "A","B", "C", "D","A","B", "C", "D", "A","B"};
var grp = myList.GroupBy( x => x );
foreach( var g in grp )
{
Console.WriteLine( "{0} {1}", g.Key, g.Count() );
}
DOTNET FIDDLE
char[] items = new[] { 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B' };
Dictionary<char, int> counts = new();
foreach(char c in items)
{
if (counts.TryGetValue(c, out int n))
{
counts[c] = n + 1;
}
else
{
counts.Add(c, 1);
}
}
While not a one liner, a simple and fast option.
I may need to use a tuple or a class, but I am not fully sure how to implement either...
Since you mentioned you may want to use a class, here is an example:
public class TextCount
{
public string Text { get; set; }
public int Count { get; set; }
}
public class Program
{
public static void Main()
{
// Initialize the list of strings
List<string> data = new List<string> { "A", "B", "C", "D", "A", "B", "C", "D", "A", "B" };
// Use LINQ to group the strings by their value and count the number of occurrences of each string
List<TextCount> result = data
.GroupBy(s => s)
.Select(g => new TextCount { Text = g.Key, Count = g.Count() })
.ToList();
// Print the results
foreach (TextCount sc in result)
{
Console.WriteLine("{0}: {1}", sc.Text, sc.Count);
}
}
}
Demo: https://dotnetfiddle.net/2FRBbK
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 SortedList of Lists and I am interested in finding the KEY that corresponds to the longest list (list with the most items in it). In code, that looks like:
// how the list is defined:
var myList = new SortedList<long, List<string>>();
// EXAMPLE data only:
myList.Add(0, new List<string>());
myList[0].AddRange(new []{"a", "b", "c"});
myList.Add(8, new List<string>());
myList[8].AddRange(new []{"1", "2"});
myList.Add(23, new List<string>());
myList[23].AddRange(new []{"c", "d", "e", "f", "g"});
In the above example the result should be "23" since that is the key that goes with the longest list.
I know how to write this with a for loop, but I think this should be a simple to do with LINQ. That said, I can't seem to get the syntax quite right! Any help is appreciated!
There's maybe a more efficient way, but you can order by count (of value) descending, and take first.
myList.OrderByDescending(m => m.Value.Count()).First().Key;
of course, if you want all the keys with highest count (they may be multiple values with same length), you should do a group by count.
Something like that.
myList.GroupBy(m => m.Value.Count())
.OrderByDescending(m => m.Key)//I'm the key of the group by
.First()
.Select(g => g.Key);//I'm the key of the SortedList
So if you add to your sample an item with same list length
myList.Add(24, new List<string>());
myList[24].AddRange(new[] {"a", "b", "c", "d", "e"});
you will get 23 And 24.
same could be achieved with
from item in myList
let maxCount = myList.Max(x => x.Value.Count())
where item.Value.Count() == maxCount
select item.Key;
Whilst a sort will give you correct results, it requires O(n log n) time to execute, which is asymptotically higher than a simple O(n) sweep:
int maxLength = myList.Max(x => x.Value.Count);
var longestKeys = myList.Where(x => x.Value.Count == maxLength).Select(x => x.Key);
using MaxBy of morelinq
var key = myList.MaxBy(x => x.Value.Count()).Key;
Just for the sake of adding yet another way of doing it, you can achieve this with Linq's Aggregate method like so:
//Extension method
public static long MaxIndex(this SortedList<long, List<string>> list)
{
return list.Aggregate(
new { MaxValue = -1, Key = -1L },
((agg, current) => (current.Value.Count.CompareTo(agg.MaxValue) > 0 || agg.Key == -1) ?
new { MaxValue = current.Value.Count, Key = current.Key } :
new { MaxValue = agg.MaxValue, Key = agg.Key })).
Key;
}
// how the list is defined:
var myList = new SortedList<long, List<string>>();
// EXAMPLE data only:
myList.Add(0, new List<string>());
myList[0].AddRange(new[] { "a", "b", "c" });
myList.Add(8, new List<string>());
myList[8].AddRange(new[] { "1", "2" });
myList.Add(23, new List<string>());
myList[23].AddRange(new[] { "c", "d", "e", "f", "g" });
var idx = myList.MaxIndex();
This is adapted from this SO answer: https://stackoverflow.com/a/15068695/172769
Cheers
I have a list consisting of duplicates, I want to create an output showing them in rank. e.g tom at the top because he has 100 entries and lilly at the bottom because it has 0.
is this possible to display a list with a rank corosponding to the number of times they appear in the list?
also display the number of times they appear next to them?
The following will give an IEnumerable where each item contains a representative item from the group and the number of times it occurs.
myList
.GroupBy( item => item.Name )
.OrderByDescending( g => g.Count() )
.Select( g => new {item=g.First(), numOcurrences=g.Count() )
var query = File
.ReadLines("input.txt")
.GroupBy(x => x)
.Select(g => new { Key = g.Key, Count = g.Count() })
.OrderByDescending(i => i.Count)
.Take(20);
foreach (var item in query)
{
Console.WriteLine("{0,5} {1}", item.Count, item.Key);
}
var groups = list.GroupBy(item => item);
var orderedGroups = groups.OrderByDescending(group => group.Count());
foreach (var group in orderedGroups)
{
Console.WriteLine(string.Format("{0} - {1}", group.Key, group.Count());
}
Here's a simple LINQ query that does remotely that.
List<string> list = new List<string> { "a", "b", "a", "c", "a", "d", "c" };
var counts = from item in list
group item by item
into grp
orderby grp.Count() descending
select new
{
Value = grp.Key,
Count = grp.Count()
};
foreach (var item in counts)
Console.WriteLine("{0} ({1})", item.Value, item.Count);
Is there a simple way to count the number of occurrences of all elements of a list into that same list in C#?
Something like this:
using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;
string Occur;
List<string> Words = new List<string>();
List<string> Occurrences = new List<string>();
// ~170 elements added. . .
for (int i = 0;i<Words.Count;i++){
Words = Words.Distinct().ToList();
for (int ii = 0;ii<Words.Count;ii++){Occur = new Regex(Words[ii]).Matches(Words[]).Count;}
Occurrences.Add (Occur);
Console.Write("{0} ({1}), ", Words[i], Occurrences[i]);
}
}
How about something like this ...
var l1 = new List<int>() { 1,2,3,4,5,2,2,2,4,4,4,1 };
var g = l1.GroupBy( i => i );
foreach( var grp in g )
{
Console.WriteLine( "{0} {1}", grp.Key, grp.Count() );
}
Edit per comment: I will try and do this justice. :)
In my example, it's a Func<int, TKey> because my list is ints. So, I'm telling GroupBy how to group my items. The Func takes a int and returns the the key for my grouping. In this case, I will get an IGrouping<int,int> (a grouping of ints keyed by an int). If I changed it to (i => i.ToString() ) for example, I would be keying my grouping by a string. You can imagine a less trivial example than keying by "1", "2", "3" ... maybe I make a function that returns "one", "two", "three" to be my keys ...
private string SampleMethod( int i )
{
// magically return "One" if i == 1, "Two" if i == 2, etc.
}
So, that's a Func that would take an int and return a string, just like ...
i => // magically return "One" if i == 1, "Two" if i == 2, etc.
But, since the original question called for knowing the original list value and it's count, I just used an integer to key my integer grouping to make my example simpler.
You can do something like this to count from a list of things.
IList<String> names = new List<string>() { "ToString", "Format" };
IEnumerable<String> methodNames = typeof(String).GetMethods().Select(x => x.Name);
int count = methodNames.Where(x => names.Contains(x)).Count();
To count a single element
string occur = "Test1";
IList<String> words = new List<string>() {"Test1","Test2","Test3","Test1"};
int count = words.Where(x => x.Equals(occur)).Count();
var wordCount =
from word in words
group word by word into g
select new { g.Key, Count = g.Count() };
This is taken from one of the examples in the linqpad
public void printsOccurences(List<String> words)
{
var selectQuery =
from word in words
group word by word into g
select new {Word = g.Key, Count = g.Count()};
foreach(var word in selectQuery)
Console.WriteLine($"{word.Word}: {word.Count}");*emphasized text*
}
This is a version which avoids Linq but uses only slightly more code.
// using System.Collections.Generic;
Dictionary<int, int> oGroups = new Dictionary<int, int>();
List<int> oList = new List<int>() { 1, 2, 3, 4, 5, 2, 2, 2, 4, 4, 4, 1 };
foreach (int iCurrentValue in oList)
{
if (oGroups.ContainsKey(iCurrentValue))
oGroups[iCurrentValue]++;
else
oGroups.Add(iCurrentValue, 1);
}
foreach (KeyValuePair<int, int> oGroup in oGroups)
{
Console.WriteLine($"Value {oGroup.Key} appears {oGroup.Value} times.");
}
this code returns a dictionary that contain the world and the occurrence:
var wordsDic = Words
.GroupBy(p => p)
.ToDictionary(p => p.Key, q => q.Count());
Your outer loop is looping over all the words in the list. It's unnecessary and will cause you problems. Remove it and it should work properly.