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.
Related
I have a LogData List and which is designed like below.
public class LogDataEntity
{
public string IndexPattern;
public int Type;
public LogDataEntity(string pattern , int type)
{
IndexPattern = pattern;
Type = type;
}
}
List<LogDataEntity> list = new List<LogDataEntity>();
list.add(new LogDataEntity("1,2,9,10", 2));
list.add(new LogDataEntity("1,10", 1));
list.add(new LogDataEntity("1,2,3", 2));
list.add(new LogDataEntity("3,9,10", 3));
list.add(new LogDataEntity("9,10", 2));
list.add(new LogDataEntity("9,10", 2));
And i want the result like below.
[Index] [Type] [Count]
10 : 2 3
9 : 2 3
1 : 2 2
3 : 1 2
2 : 2 2
3 : 3 1
9 : 3 1
10 : 3 1
1 : 1 1
I want to group by and count not only splited string(indexpattern) but also
type too. And i want to count and show them by OrderByDescending(Count).
I think There is multiple group by.
How should i do this with Linq?
You can use SelectMany to create list of (Index, Type) pairs, then group by and count to do the rest:
var pairs = data.SelectMany(x => x.IndexPattern
.Split(",")
.Select(y => new {Index = y, Type = x.Type});
var res = from p in pairs
group p by new { p.Index, p.Type } into grp
select new {
Index = grp.Key.Index,
grp.Key.Type,
grp.Count()
};
(An order by clause can be added before the final Select as required.)
You've, probably, stuck in SelectMany; all the other commands are quite evident:
var result = list
.SelectMany(record => record
.IndexPattern
.Split(',')
.Select(item => {
index = item,
type = record.Type,
}))
.GroupBy(item => item)
.OrderByDescending(chunk => chunk.Count())
.Select(chunk => $"{chunk.index,-10} : {chunk.type,-10} {chunk.Count()}");
Console.WriteLine(string.Join(Environment.NewLine, result));
This is improve version or previous answers.
var pairs = Logs.SelectMany(x => x.IndexPattern.Split(',').Select(y => new {
Index = y, Type= x.Type }));
var pairs2 = (from p in pairs group p by p into grp select new { Index =
grp.Key.Index, Reason = grp.Key.Type, Count = grp.Count() }
).OrderByDescending(p => p.Count);
foreach (var i in pairs2)
{
//print with i
}
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 a list of model class with some fields in it,I want to apply group by and count on the base of ID on it,in my lst I have data like this
lst[0]=Id=10,Name="user1",Gender="Male",Course="course1";
lst[1]=Id=10,Name="user1",Gender="Male",Course="course2";
lst[2]=Id=10,Name="user1",Gender="Male",Course="course3";
lst[3]=Id=11,Name="user2",Gender="Male",Course="course4";
I want to count the course with group by on Id.
This is how I am doing
var result = lst.GroupBy(n => n.Id).Select(c => new { Key = c.Key, total = c.Count()});
But after applying this I am not getting any value in result variable(ie result.Name or result[0].Name)
How can I apply group by and count in this list so I get the list data after applying the group by too.
Having used GroupBy you have projected (Select(..)) to an anonymous object which throws away the selected items in the group; your result will be an IEnumerable<anonymous> of items with properties Key and total
var result = lst.GroupBy(n => n.Id)
.Select(c => new { Key = c.Key, total = c.Count()});
If you want to keep the selected items, you need to include this in your projection
var result = lst.GroupBy(n => n.Id)
.Select(c => new { Key = c.Key, total = c.Count(), Items = c});
foreach(var r in result)
{
Console.WriteLine("Key: {0} Count:{1}",r.Key,r.total)
foreach(var i in r.Items)
Console.WriteLine(i.Name);
}
The reason is because you are grouping by Id and just provinding an anonymous object with Key (which is the Id) and a Count. If you want to read the values, each item of the result group set will have the Count and the values where you can loop between them. For sample:
var result = lst.GroupBy(n => n.Id);
foreach (var g in result)
{
Console.WriteLine("For group {0} the total is {1}.", g.Key, g.Count());
foreach(var item in g)
{
Console.WriteLine("Name: {0}", item.Name);
}
}
All you have to do is call ToList or ToArray.
var result = lst.GroupBy(n => n.Id)
.Select(c => new
{
Key = c.Key,
total = c.Count(),
Gender = c.First().Gender
}).ToList();
Or alternativly - to avoid multiple iterations of the same enumeration:
var result = lst.GroupBy(n => n.Id)
.Select(c =>
{
var firstEl = c.First();
return new {
Key = c.Key,
total = c.Count(),
Gender = firstEl.Gender,
Name = firstEl.Name,
Course = firstEl.Course
}
}).ToList();
Select will only return a query which is deferredly executed. This means you get the result when you actually iterate on the enumeration, which is enforeced by calling one of the mentioned methods.
i like to confess that i am weak in LINQ.
i have list with data. i want to search list fist by given value and then sort data by max occurance means which comes maximum time in rows.
List<SearchResult> list = new List<SearchResult>() {
new SearchResult(){ID=1,Title="Cat"},
new SearchResult(){ID=2,Title="dog"},
new SearchResult(){ID=3,Title="Tiger"},
new SearchResult(){ID=4,Title="Cat"},
new SearchResult(){ID=5,Title="cat"},
new SearchResult(){ID=6,Title="dog"},
};
if i search & sort list with data like "dog cat" then output will be like
ID=1,Title=Cat
ID=4,Title=Cat
ID=5,Title=Cat
ID=2,Title=dog
ID=6,Title=dog
all cat will come first because this cat keyword found maximum time in all the rows and then dog found maximum time.
this below data will not come because it is not in search term
ID=3,Title=Tiger
looking for solution. thanks
UPDATE PORTION CODE
List<SearchResult> list = new List<SearchResult>() {
new SearchResult(){ID=1,Title="Geo Prism 1995 - ABS #16213899"},
new SearchResult(){ID=2,Title="Excavator JCB - ECU P/N: 728/35700"},
new SearchResult(){ID=3,Title="Geo Prism 1995 - ABS #16213899"},
new SearchResult(){ID=4,Title="JCB Excavator - ECU P/N: 728/35700"},
new SearchResult(){ID=5,Title="Geo Prism 1995 - ABS #16213899"},
new SearchResult(){ID=6,Title="dog"},
};
var to_search = new[] { "Geo", "JCB" };
var result = list.Where(sr => to_search.Any(ts => String.Compare(ts, sr.Title, StringComparison.OrdinalIgnoreCase) == 0))
.GroupBy(sr => sr.Title.ToLower())
.OrderByDescending(g => g.Count());
var matched = result.SelectMany(m => m);
var completeList = matched.Concat(list.Except(matched));
dataGridView2.DataSource = completeList.ToList();
i try to your logic in another apps but it is not working. according to logic three rows first come with GEO keyword and then next 2 rows comes with JCB and then unmatched rest comes. what i need to change in ur code. please help. thanks
This will filter your list and group it by Title, sorting the groups by their size.
List<SearchResult> list = new List<SearchResult>() {
new SearchResult(){ID=1,Title="Cat"},
new SearchResult(){ID=2,Title="dog"},
new SearchResult(){ID=3,Title="Tiger"},
new SearchResult(){ID=4,Title="Cat"},
new SearchResult(){ID=5,Title="cat"},
new SearchResult(){ID=6,Title="dog"},
};
var to_search = new[] { "cat", "dog" };
var result = list.Where(sr => to_search.Any(ts => String.Compare(ts, sr.Title, StringComparison.OrdinalIgnoreCase) == 0))
.GroupBy(sr => sr.Title.ToLower())
.OrderByDescending(g => g.Count());
foreach (var group in result)
foreach (var element in group)
Debug.WriteLine(String.Format("ID={0},Title={1}", element.ID, element.Title));
Output:
ID=1,Title=Cat
ID=4,Title=Cat
ID=5,Title=cat
ID=2,Title=dog
ID=6,Title=dog
If you don't care about the actual grouping, just can flatten the list of groups with SelectMany.
(Note that this code will ignore the case of Title. I don't know if this is what you want or if it is a typo in code: you are using cat and Cat, and in your output it is only Cat, but dog is not capitalized.)
Edit:
To get the unmatched items, you can use Except:
var unmatched = list.Except(result.SelectMany(m => m)); // beware! contains the tiger!
Edit 2:
var result = list.Where(sr => to_search.Any(ts => String.Compare(ts, sr.Title, StringComparison.OrdinalIgnoreCase) == 0))
.GroupBy(sr => sr.Title.ToLower())
.OrderByDescending(g => g.Count());
var matched = result.SelectMany(m => m);
var completeList = matched.Concat(list.Except(matched));
foreach (var element in completeList)
Debug.WriteLine(String.Format("ID={0},Title={1}", element.ID, element.Title));
Output
ID=1,Title=Cat
ID=4,Title=Cat
ID=5,Title=cat
ID=2,Title=dog
ID=6,Title=dog
ID=3,Title=Tiger
Edit 3
var result = from searchResult in list
let key_string = to_search.FirstOrDefault(ts => searchResult.Title.ToLower().Contains(ts.ToLower()))
group searchResult by key_string into Group
orderby Group.Count() descending
select Group;
IEnumerable<string> pets = new[] { "Cat", "dog", "Tiger", "Cat", "cat", "dog" };
var test = pets
.Where(p=>p.ToUpperInvariant() == "CAT" || p.ToUpperInvariant() == "DOG")
.GroupBy(p => p.ToUpperInvariant())
.OrderByDescending(g => g.Count())
.SelectMany(p => p);
foreach (string pet in test)
Console.WriteLine(pet);
I think the following should do the trick.
var searchText = "cat dog";
var searchResult = list
.Select(i => new {
Item = i,
Count = list.Count(x => string.Compare(x.Title, i.Title, true) == 0) // Add counter
})
.Where(i => searchText.Contains(i.Item.Title))
.OrderByDescending(i => i.Count)
.ThenBy(i => i.Item.ID)
.ToList()
Update
If you want unmatched data at the end, you need to add another sorting property in the anonymous object.
var searchText = "cat dog";
var searchResult = list
.Select(i => new {
Item = i,
Matched = searchText.Contains(i.Item.Title.ToUpper()),
Count = list.Count(x => string.Compare(x.Title, i.Title, true) == 0) // Add counter
})
.OrderByDescending(i => i.Matched)
.ThenBy(i => i.Count)
.ThenBy(i => i.Item.ID)
.ToList()
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());