I'm working on a WPF application, and at one point, I have to get/show all the duplicates from the string list. (With the duplicated strings name and the number of how many of that same string is in the list)Like this for example: "The list contains the String 'Hello' 3 times." So far, I'm getting the string's name successfully but I can't manage to get the correct number of times it is presented in the list.
This is my code so far:
List<String> answerData = new List<String>();
using (MySqlCommand command = new MySqlCommand(query2, conn))
{
using (MySqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
answerData.Add(reader.GetString(0));
}
}
}
var duplicates = answerData
.GroupBy(i => i)
.Where(g => g.Count() > 1)
.Select(g => g.Key);
foreach (var d in duplicates)
{
MessageBox.Show(""+ d + duplicates.Count().ToString()); //Here I tried to get the number
//with Count() but it doesn't work as I thought it would.
}
What should I add/change to get the result I want?
EDIT
As suggested changed my code to the following:
var duplicates = answerData
.GroupBy(i => i)
.Where(g => g.Count() > 1);
foreach (var d in duplicates)
{
MessageBox.Show(d.Key + " " + d.Count().ToString());
}
And now it works smoothly.
Thank you everyone!
Store the actual groups instead of the keys in duplicates:
var duplicates = answerData
.GroupBy(i => i)
.Where(g => g.Count() > 1);
You could then iterate through the groups:
foreach (var d in duplicates)
{
MessageBox.Show(d.Key + " " + d.Count().ToString());
}
This example counts, i.e. iterates, each group twice. Alternatively, you could store objects that contain both the Key and the Count as suggested by #HimBromBeere.
You just need to return the number within your Select:
var duplicates = answerData
.GroupBy(i => i)
.Select(g => new { Key = g.Key, Count = x.Count() })
.Where(x => x.Count > 1);
Notice that I changed the order of your statements to avoid a duplicate execution of g.Count().
You can do something like this
you need to use Dictionary for performance reasons
List<String> answerData = new List<String>();
Dictionary<string,int> map = new Dictionary<string, int>();
foreach (var data in answerData)
{
if (map.ContainsKey(data))
{
map[data]++;
}
else
{
map.Add(data, 1);
}
}
foreach (var item in map)
{
if (item.Value > 1)
{
Console.WriteLine("{0} - {1}", item.Key, item.Value);
}
}
Related
I'm trying to make a listbox where i can add items to it using datagridview, the thing is i want to determine which item is duplicate and how many times it duplicate.
item1
item1
item2
item2
item2
output item1=2, item2=3
Here is the one that i tried which shows the last item that have been duplicate
int count = 0;
for (int i = 0; i < listBox1.Items.Count; i++)
{
var s = listBox1.Items[i].ToString();
if (s.StartsWith(listfood))
{
if (s == listfood)
{
++count;
}
}
}
MessageBox.Show(count.ToString());
Try
var duplicateItems = listBox1.Items.GroupBy(x => x.ToString())
.Where(x => x.Count() > 1)
.Select(x => new { Value = x.Key, Count = x.Count() })
.ToList();
using System.Linq;
// ...
var duplicates = listBox1.Items.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(y => new { ItemName = y.Key, Occurrences = y.Count() })
.ToList();
foreach (var duplicate in duplicates)
MessageBox.Show($"{duplicate.ItemName}: {duplicate.Occurrences}");
This solution uses LINQ to query the listBox1's Items collection and filter out any data we don't care about.
First, we use GroupBy to sort the items. Then, Where will filter out any items in the collection that only exist once. Select allows us to "project" the items remaining in the filtered collection into a "new form" (we use an anonymous type with an ItemName and Occurrences property to track the names of the duplicates and the number of times it appears in the collection).
Finally, ToList converts the collection from an IEnumerable<string> to aListtype.ToListis optional depending on how you plan on usingduplicates. In fact, my example doesn't need to callToListbecause aforeachloop can iterate over anIEnumerable` collection.
I know the Answers above will definitely work, but i can't understand it and make it work. This one works for me, where i transfer values of listbox to an array and check the duplicates inside that array.
var list = new List<string>();
foreach(var item in listBox1.Items)
{
list.Add(item.ToString());
}
var r = from b in list
group b by b into g
let count = g.Count()
orderby count descending
select new { Value = g.Key, Count = count };
foreach(var x in q)
{
MessageBox.Show("value: " + b.Value + " Count:" + b.Count);
}
Found my answer here
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 + "");
}
I have this code
var myBuilder = new StringBuilder();
foreach (var item in myList)
{
myBuilder.Append(item.Number).Append(" - ").Append(item.SecondNumber).Append(", ");
}
var text = myBuilder;
and using that I'm getting this text
AAA08 - BB08, AAA09 - BB09, AAA09 - BB10,
myList returns this:
{ Number = "AAA08", SecondNumber = "BB08" }
{ Number = "AAA09", SecondNumber = "BB09" }
{ Number = "AAA09", SecondNumber = "BB10" }
How can I concatenate it to string to display this:
AAA08 - BB08; AAA09 - BB09, BB10
I can do replace , to ; but can't get how to group these Numberto display only one and each SecondNumber right to him
You can do this with Linq and string.Join
var grouped = myList
.GroupBy(x => x.Number)
.Select(g => g.Key + " - " + string.Join(", ", g.Select(x => x.SecondNumber)))
var text = string.Join("; ", grouped);
You can use GroupBy and String.Join:
var groupedNumbers= myList
.GroupBy(x => x.Number)
.Select(g => $"{g.Key} - {String.Join(", ", g.Select(x => x.SecondNumber))}");
string result = String.Join("; ", groupedNumbers);
I'm using string interpolation which is a C#6 feature, if you aren't using it replace $"{g.Key}..." with String.Format("{0}...", g.Key, ..).
When you get Number, iterate through myList to find all instances of Number, then get each corresponding SecondNumber and append it to your string.
Before building final string you probably would need to group your array first with this code:
var groupedList = myList.GroupBy(x => x.Number)
and later use that grouped list to get proper string:
foreach (var item in groupedList)
{
myBuilder.Append(item.Number).Append(" - ").Append(item.Select(x => x.SecondNumber).Join(", ")).Append("; ");
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I am trying to get the count for the most common duplicate item in a list.
So far, I have:
List<string> brandList = new List<string>();
which contains 5 different soft drink brands, and the count is 100. I need to find out which brand has the most duplicates in the list and count how many duplicates there are.
Presuming that your pseudo code actually was:
List<Brand> brandList=new List<Brand>();
// fill list
and your Brand class either overrides Equals+getHashCode or has a property like BrandID which is the identifier. N youow want to get the count of the most popular brand, you can use LINQ:
var mostPopularBrand = brandList.GroupBy(b => g.BrandID)
.OrderByDescending(g => g.Count())
.Select(g => new { BrandID = g.Key, Count = g.Count()})
.First();
Console.WriteLine("Most poular brand: {0} Count: {1}",
mostPopularBrand.BrandID, mostPopularBrand.Count);
Update: If it's actually a List<string>(question was edited):
var mostPopularBrand = brandList.GroupBy(str => str)
.OrderByDescending(g => g.Count())
.Select(g => new { Brand = g.Key, Count = g.Count()})
.First();
Your code doesn't compile. Providing that you mean List<String> as a storage of brands:
var ListbrandList = new List<String>() {
"Cola",
"Juice",
"Cola",
"Water",
"Milk",
"Water",
"Cola",
};
var result = ListbrandList
.GroupBy(item => item)
.Select(item => new {
Name = item.Key,
Count = item.Count()
})
.OrderByDescending(item => item.Count)
.ThenBy(item => item.Name);
String report = String.Join(Environment.NewLine, result
.Select(item => String.Format("{0} appears {1} time(s)", item.Name, item.Count)));
you'll have report as
Cola appears 3 time(s)
Water appears 2 time(s)
Juice appears 1 time(s)
Milk appears 1 time(s)
var result = brandList
.Distinct()
.GroupJoin(brand,
k => k,
b => b,
(k, b) => new { BrandName = k, Count = b.Count() });
// An usage could be...
foreach (var r in result)
{
Debug.WriteLine("{0} Brand has {1}", r.BrandName, r.Count);
}
Without LinQ:
var result = new Dictionary<string, int>();
foreach (var brand in brandList)
{
if (!result.ContainsKey(brand))
{
var count = brandList.FindAll(x => x.Equals(brand)).Count;
result.Add(brand, count);
}
}
foreach (var r in result)
{
Console.WriteLine("{0} Brand has {1}", r.Key, r.Value);
}
You can try to do group using LINQ by Elements and calculate count
var groupedList = from l in ListbrandList
group l by l into grp
select new { key = grp.Key, cnt = grp.Count()};
Then you will have group by key (Element) and value (Count)
Something like this maybe:
var res = brandList.GroupyBy(x => x)
.Select(x => new {Key = x.Key, Count = x.Count});
Now you have a list containing the actual drink-number and the count for this number.
EDIT: Getting the brand with most count is now easy, e.g. by using:
var mostPopular = res.Single(x => x.Count == res.Max(y => y.Count));
EDIT2: Without LINQ the following might work, which is way longer and more complicated:
// get the count for every brand
Dictionary<string, int> res = new Dictionary<string, int>();
foreach(var x in brands)
{
if (!res.ContainsKey(x)) res[x] = 0;
res[x]++;
}
// now get the max count
int currentMax = 0;
string key = "";
foreach (var kv in res)
{
if (kv.Value > currentMax)
{
currentMax = kv.Value;
key = kv.Key;
}
}
Now the key should contain the brand with highest Count.
To count element with specific value in a list use :
int quantity = lst.Count(r => r == "Cola");
Example :
List<string> lst = new List<string>()
{
"Sprite",
"Cola",
"Sprite",
"Sprite",
"Cola",
"Sprite",
"Sprite",
"Cola",
"Pepsi",
"Sprite",
"Pepsi",
"Sprite",
};
string[] popularBrands = { "Cola", "Pepsi" };
int[] quantities = new int[popularBrands.Length];
for (int i = 0; i < popularBrands.Length; i++)
{
quantities[i] = lst.Count(r => r.ToUpper() == popularBrands[i].ToUpper());
Console.WriteLine("{0} : {1}", popularBrands[i], quantities[i]);
}
Output
Cola : 3
Pepsi : 2
P.S.
About this code r => r.ToUpper() == popularBrands[i].ToUpper() :
r is variable that holds a value from our list (that are taken one by one). We also use ToUpper() to make sure that our check is case insensitive.
So we basically loop through the collection taking values out of it one by one. Each time we put value to r variable and check if this variable satisfies condition. If so - we count it, if not - we just move to next value.
I have a list of string items declared like this:
List<string> methodList = new List<string>();
I do what I need to get done and the results are something like this:
Method1;15
Method2;30
Method3;45
Method1;60
I need to loop through this list and display a distinct list and the addition of the totals. Something like this:
Method1 75
Method2 30
Method3 45
In order to do this, you'll need to split this up, then sum:
var results = methodList
.Select(l => l.Split(';'))
.GroupBy(a => a[0])
.Select(g =>
new
{
Group = g.Key,
Count = g.Count(),
Total = g.Sum(arr => Int32.Parse(arr[1]))
});
foreach(var result in results)
Console.WriteLine("{0} {1} {2}", result.Group, result.Count, result.Total);
Something like this would work:
var sumList = methodList.Select( x=>
{
var parts = x.Split(';');
return new
{
Method = parts [0],
Cost = Convert.ToInt32(parts[1])
};
})
.GroupBy( x=> x.Method)
.Select( g=> new { Method = g.Key, Sum = g.Sum( x=> x.Cost) })
.ToList();
foreach(var item in sumList)
Console.WriteLine("Total for {0}:{1}", item.Method, item.Sum);
A better approach would be to keep the individual methods and their cost in a strongly typed class, so you don't have to do string parsing to operate them:
public class MethodCost
{
public string MethodName { get; set; }
public int Cost { get; set; }
}
Now you can use a List<MethodCost> instead and have direct access to the cost - use strings for presentation (i.e. writing to the console), not for internal storage when it is not appropriate.
Try the following
var result = methodList
.Select(
x =>
{
var items = x.Split(';');
return new { Name = items[0], Value = Int32.Parse(items[1]) };
})
.GroupBy(pair => pair.Name)
.Select(
grouping =>
{
var sum = grouping.Sum(x => x.Value);
return String.Format("{0} {1}", grouping.Key, sum);
});
Yet another, slightly compacter variant is
var results = from item in methodList
let parts = item.Split(';')
group Int32.Parse(parts[1]) by parts[0];
foreach(var item in results)
Console.WriteLine("{0} {1}", item.Key, item.Sum());