A method to count occurrences in a list - c#

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.

Related

Removing strings with duplicate letters from string array

I have array of strings like
string[] A = { "abc", "cccc", "fgaeg", "def" };
I would like to obtain a list or array of strings where any letter appears only one time. I means that "cccc", "fgaeg" will be removed from input array.
I managed to do this but I feel that my way is very messy, unnecessarily complicated and not efficient.
Do you have any ideas to improve this algorythm (possibliy replacing with only one Linq query)?
My code:
var goodStrings = new List<string>();
int i = 0;
foreach (var str in A)
{
var tempArr = str.GroupBy(x => x)
.Select(x => new
{
Cnt = x.Count(),
Str = x.Key
}).ToArray();
var resultArr = tempArr.Where(g => g.Cnt > 1).Select(f => f.Str).ToArray();
if(resultArr.Length==0) goodStrings.Add(A[i]);
i++;
}
You can use Distinct method for every array item and get items with count of distinct items equals to original string length
string[] A = { "abc", "cccc", "fgaeg", "def" };
var result = A.Where(a => a.Distinct().Count() == a.Length).ToList();
You'll get list with abc and def values, as expected

find item in list , and get other item from other list at same location using linq

I have a class where it has a collection of list. I want to search a parameter inside one of the list. So the location that I found the list, I want to get at the same location at other list in the same class...
How to achieve this?
void Main()
{
var myListCollectionObj = new myListCollection();
Console.WriteLine(myListCollectionObj.firstList);
Console.WriteLine(myListCollectionObj.secondList);
Console.WriteLine(myListCollectionObj.thirdList);
var testFirstList = myListCollectionObj.firstList.Where(x => x == 3); //then i want to get "33", and 333 from secondList and thirdList respectively
Console.WriteLine(testFirstList);
}
class myListCollection
{
public List<int> firstList = new List<int>(){ 1, 2, 3, 4, 5};
public List<string> secondList = new List<string>(){ "11", "22", "33", "44", "55"};
public List<int> thirdList = new List<int>(){ 111, 222, 333, 444, 555};
}
int index = myListCollectionObj.firstList.IndexOf(3);
string elem2;
int elem3;
if (index >= 0 && index < myListCollectionObj.secondList.Length)
elem2 = myListCollectionObj.secondList[index]
if (index >= 0 && index < myListCollectionObj.thirdList.Length)
elem3 = myListCollectionObj.thirdList[index]
You don't need LINQ for that, only List<T>'s own IndexOf() method and indexer property:
int index = myListCollectionObj.firstList.IndexOf(3);
string secondValue = myListCollectionObj.secondList[index];
int thirdValue = myListCollectionObj.thirdList[index];
You may want to add error handling: if 3 is not contained in firstList, an index of -1 is returned by IndexOf().
I guess the best way if there are more than one 3 values would be using simple for loop:
var testFirstList = new List<int>();
var testSecondList = new List<string>();
var testThirdList = new List<int>();
for (var i = 0; i < myListCollectionObj.firstList.Length; ++i) {
if (myListCollectionObj.firstList[i] == 3) {
testFirstList.Add(myListCollectionObj.firstList[i]);
testSecondList.Add(myListCollectionObj.secondList[i]);
testThirdList.Add(myListCollectionObj.thirdList[i]);
}
}
A good guideline is that if you find yourself combining indices and LINQ, you probably have other options available. In this case, a good alternative would be using Zip
This approach lets you combine the 3 collections and act upon the resulting zipped collection as a single entity such that indices are no longer directly required.
var result = firstList.Zip(
secondList.Zip(thirdList,
(b, c) => new { b, c }),
(a, b) => new { Value1 = a, Value2 = b.b, Value3 = b.c })
.Where(x => x.Value1 == 3).ToList();
result.ForEach(v => Console.WriteLine(v));
correct me if i am wrong are you looking for the index of the item you searched in first list and then use the same index to retrieve from other list
If yes
Try this
var testFirstList = myListCollectionObj.firstList.Where(x => x == 3).FirstOrDefault(); //then i want to get "33", and 333 from secondList and thirdList respectively
var index = myListCollectionObj.firstList.IndexOf(testFirstList);

Find most common element in array

I have a string array that can contains 1 or more elements with various string values. I need to find the most common string in the array.
string aPOS[] = new string[]{"11","11","18","18","11","11"};
I need to return "11" in this case.
Try something like this using LINQ.
int mode = aPOS.GroupBy(v => v)
.OrderByDescending(g => g.Count())
.First()
.Key;
If you don't like using LINQ or are using e.g. .Net 2.0 which does not have LINQ, you can use foreach loops
string[] aPOS = new string[] { "11", "11", "18", "18", "11", "11"};
var count = new Dictionary<string, int>();
foreach (string value in aPOS)
{
if (count.ContainsKey(value))
{
count[value]++;
}
else
{
count.Add(value, 1);
}
}
string mostCommonString = String.Empty;
int highestCount = 0;
foreach (KeyValuePair<string, int> pair in count)
{
if (pair.Value > highestCount)
{
mostCommonString = pair.Key;
highestCount = pair.Value;
}
}
You can do this with LINQ, the following is untested, but it should put you on the right track
var results = aPOS.GroupBy(v=>v) // group the array by value
.Select(g => new { // for each group select the value (key) and the number of items into an anonymous object
Key = g.Key,
Count = g.Count()
})
.OrderByDescending(o=>o.Count); // order the results by count
// results contains the enumerable [{Key = "11", Count = 4}, {Key="18", Count=2}]
Here's the official Group By documentation

List<string> Simple Group and Count?

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()));

find common items across multiple lists in C#

I have two generic list :
List<string> TestList1 = new List<string>();
List<string> TestList2 = new List<string>();
TestList1.Add("1");
TestList1.Add("2");
TestList1.Add("3");
TestList2.Add("3");
TestList2.Add("4");
TestList2.Add("5");
What is the fastest way to find common items across these lists?
Assuming you use a version of .Net that has LINQ, you can use the Intersect extension method:
var CommonList = TestList1.Intersect(TestList2)
If you have lists of objects and want to get the common objects for some property then use;
var commons = TestList1.Select(s1 => s1.SomeProperty).ToList().Intersect(TestList2.Select(s2 => s2.SomeProperty).ToList()).ToList();
Note: SomeProperty refers to some criteria you want to implement.
Assuming you have LINQ available. I don't know if it's the fastest, but a clean way would be something like:
var distinctStrings = TestList1.Union(TestList2).Distinct();
var distinctStrings = TestList1.Union(TestList2);
Update: well never mind my answer, I've just learnt about Intersect as well!
According to an update in the comments, Unions apply a distinct, which makes sense now that I think about it.
You can do this by counting occurrences of all items in all lists - those items whose occurrence count is equal to the number of lists, are common to all lists:
static List<T> FindCommon<T>(IEnumerable<List<T>> lists)
{
Dictionary<T, int> map = new Dictionary<T, int>();
int listCount = 0; // number of lists
foreach (IEnumerable<T> list in lists)
{
listCount++;
foreach (T item in list)
{
// Item encountered, increment count
int currCount;
if (!map.TryGetValue(item, out currCount))
currCount = 0;
currCount++;
map[item] = currCount;
}
}
List<T> result= new List<T>();
foreach (KeyValuePair<T,int> kvp in map)
{
// Items whose occurrence count is equal to the number of lists are common to all the lists
if (kvp.Value == listCount)
result.Add(kvp.Key);
}
return result;
}
Sort both arrays and start from the top of both and compare if they are equal.
Using a hash is even faster: Put the first array in a hash, then compare every item of the second array if it is already in the hash.
I don't know those Intersect and Union are implemented. Try to find out their running time if you care about the performance. Of course they are better suited if you need clean code.
Use the Intersect method:
IEnumerable<string> result = TestList1.Intersect(TestList2);
Using HashSet for fast lookup. Here is the solution:
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
List<int> list1 = new List<int> {1, 2, 3, 4, 5, 6 };
List<int> list2 = new List<int> {1, 2, 3 };
List<int> list3 = new List<int> {1, 2 };
var lists = new IEnumerable<int>[] {list1, list2, list3 };
var commons = GetCommonItems(lists);
Console.WriteLine("Common integers:");
foreach (var c in commons)
Console.WriteLine(c);
}
static IEnumerable<T> GetCommonItems<T>(IEnumerable<T>[] lists)
{
HashSet<T> hs = new HashSet<T>(lists.First());
for (int i = 1; i < lists.Length; i++)
hs.IntersectWith(lists[i]);
return hs;
}
}
Following the lead of #logicnp on counting the number of lists containing each member, once you have your list of lists, it's pretty much one line of code:
List<int> l1, l2, l3, cmn;
List<List<int>> all;
l1 = new List<int>() { 1, 2, 3, 4, 5 };
l2 = new List<int>() { 1, 2, 3, 4 };
l3 = new List<int>() { 1, 2, 3 };
all = new List<List<int>>() { l1, l2, l3 };
cmn = all.SelectMany(x => x).Distinct()
.Where(x => all .Select(y => (y.Contains(x) ? 1 : 0))
.Sum() == all.Count).ToList();
Or, if you prefer:
public static List<T> FindCommon<T>(IEnumerable<List<T>> Lists)
{
return Lists.SelectMany(x => x).Distinct()
.Where(x => Lists.Select(y => (y.Contains(x) ? 1 : 0))
.Sum() == Lists.Count()).ToList();
}

Categories