This question already has answers here:
Find the most occurring number in a List<int>
(6 answers)
Closed 9 years ago.
List<int> a = new List<int>{ 1,1,2,2,3,4,5 };
What's the quickest way to do this with LINQ?
I'm new to LINQ
The key here is using Enumerable.GroupBy and the aggregation method Enumerable.Count:
List<int> list = new List<int>() { 1,1,2,2,3,4,5 };
// group by value and count frequency
var query = from i in list
group i by i into g
select new {g.Key, Count = g.Count()};
// compute the maximum frequency
int whatsTheFrequencyKenneth = query.Max(g => g.Count);
// find the values with that frequency
IEnumerable<int> modes = query
.Where(g => g.Count == whatsTheFrequencyKenneth)
.Select(g => g.Key);
// dump to console
foreach(var mode in modes) {
Console.WriteLine(mode);
}
Jason's answer is correct, but you can perform this operation in one LINQ operation.
List<int> list = new List<int>() { 1, 1, 2, 2, 3, 4, 5 };
// return most frequently occurring items
var query = from i in list
group i by i into g
let maxFreq = (from i2 in list
group i2 by i2 into g2
orderby g2.Count() descending
select g2.Count()).First()
let gCount = g.Count()
where gCount == maxFreq
select g.Key;
// dump to console
foreach (var mode in query)
{
Console.WriteLine(mode);
}
public static Tres MostCommon<Tsrc, Tres>(this IEnumerable<Tsrc> source, Func<Tsrc, Tres> transform)
{
return source.GroupBy(s => transform(s)).OrderByDescending(g => g.Count()).First().Key;
}
And in your example with integral types you can call it as:
List<int> a = new List<int>{ 1,1,2,2,3,4,5 };
int mostCommon = a.MostCommon(x => x);
from num in a
group num by num into numg
let c = numg.Count()
order by c descending
select new { Number = numg.Key, Count = c }
I think the most frequent number can also be achieved in a single query like this-
var query = (from i in list
group i by i into g
orderby g.Count() descending
select new { Key = g.Key, Count = g.Count() }).FirstOrDefault();
if (query == null) Console.WriteLine("query = NULL");
else Console.WriteLine("The number '{0}' occurs {1} times.", query.Key, query.Count);
Null check is not really required but it may be useful when null is actually expected (like Empty list?)
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 simple list of int with three elements, all of them are set to 1000.
If I group this list by values, I still get three elements instead of just one.
Why?
var l = new List<int> {1000, 1000, 1000};
var gr = from i in l
group i by new
{
j = i
}
into g1
from g in g1
select new
{
Id = g1.Key.j
};
var count = gr.Count(); // <- count is 3!
Loose the second from
var l = new List<int> {1000, 1000, 1000};
var gr = from i in l
group i by new
{
j = i
}
into g1
select new
{
Id = g1.Key.j
};
var count = gr.Count(); // <- count is 1!
That's because you are again projecting the grouped items. It is returning IEnumerable<IGrouping<int,int>> and you are enumerating IEnumerable<int> with from g in g1 which means you will get the items which are in group 1000 and thus the count 3.
Following query will give you correct result:-
var gr = from i in l
group i by i
into g1
select new
{
Id = g1.Key
};
gr.Count() will be 1 here since we are projecting the Key and not the items inside that group.
With Method Syntax:
You currect query is : l.GroupBy(x => x).SelectMany(x => x) so it will project all the items in the group thus Count 3.
If you want to count the Key then: l.GroupBy(x => x).Select(x => x.Key) this will return 1 since it will create a single group of 1000.
I think you're trying to achieve this:
var l = new List<pairs>
{
new pairs {Index = 0, Value = 1000},
new pairs {Index = 1, Value = 1000},
new pairs {Index = 2, Value = 1000},
};
var gr = l.GroupBy(a => a.Value);
var count = gr.Count(); // <- count is 1
Where Pairs is a simple POCO:
internal class pairs
{
public int Value { get; set; }
public int Index { get; set; }
}
The group by clause gives you the key along with all of the items that are in the group. You are selecting the items and not the key.
For example, try this:
var gr = from i in l
group i by i into g1
select g1;
var count = gr.Count();
var itemCount = gr.First().Count();
I want to sort a large integer array into 2 groups, i.e. 1 group the multiples of 4 and the other group the multiples of 5. How can I do this using just one query? Keep an eye on the performance which is really important in my case.
To further explain what I need, suppose my list of numbers is { 2, 7, 8, 10, 12, 14, 19,20, 25} then I would expect my output to be this:
new[]
{
new
{
Remainder = 4,
Numbers = new List<int>(){ 8, 12, 20}
},
new
{
Remainder = 5,
Numbers = new List<int>(){10, 20, 25}
}
}
Here's what I have gotten so far:
var numberGroupsTimes5 =
from n in numbers
group n by n % 5 into g
where g.Key == 0
select new { Remainder = g.Key, Numbers = g };
var numberGroupsTimes4 =
from n in numbers
group n by n % 4 into g
where g.Key == 0
select new { Remainder = g.Key, Numbers = g };
As you can see it gets me close with 2 queries but as I said I would like a single query.
You could use Concat:
var something = numberGroupsTimes5.Concat(numberGroupsTimes4);
to simply concatenate two sequences.
It's not entire clear why you use a GroupBy, then filter for Key == 0. Remainder will always be 0.
Maybe a simple Where is enough?
You can simply "combine" your queries by using a logical OR (||):
var something = numbers.Where(x => x%4 == 0 || x%5 == 0);
In response to your comment: Are you looking for something like this?
var result = new[] {4, 5}
.Select(d => new
{
Divider = d,
Values = numbers.Where(n => n % d == 0).ToList()
});
Do you mean?
var numberGroupsTimes4or5 = from n in numbers
group n by n into g
where g.Key % 4 == 0 || g.Key % 5 == 0
select new { Remainder = g.Key, Numbers = g };
Maybe this?
var result = new[] { 4, 5 }
.SelectMany(x => numbers.Select(n => (n, x)))
.Where(g => g.n % g.x == 0)
.GroupBy(g => g.x, (Key, g) =>
new { Remainder = Key, Numbers = g.Select(z => z.n) });
which gives this result
Here is a similar approach but this time using a query syntax like in your question.
var numbersAndRemainders = new[] { 4, 5 }
.SelectMany(rem => numbers.Select(n => (n, rem)));
var numberGroups =
from n in numbersAndRemainders
group n by new { remainder = n.n % n.rem, n.rem } into g
where g.Key.remainder == 0
select new { Remainder = g.Key.rem, Numbers = g.Select(z => z.n) };
There are two LINQ methods you could use for this:
//This will join the lists, excluding values that already appear once
var result = numberGroupsTimes5.Union(numberGroupsTimes4)
//This will simply append one list the the other
var result = numberGroupsTimes5.Concat(numberGroupsTimes4)
I have multiple list of int for the ID that are merged together. i need to get the ID that matches the total number of duplicates with a certain number. for example:
int g=3;
List<int> mainlist = new List<int>();
List<int> list1 = new List<int>{1,2,3,4,5,6,7,8,9};
List<int> list2 = new List<int>{2,3,4,5,7,8,9};
List<int> list3 = new List<int>{1,3,5,6,7,9};
mainlist = list1.Concat(list2).Concat(list3).Tolist();
I want to get the ID where the number of duplicate is equal to g
You can group your mainlist and select only these groups where Count() == g:
var IDs = mainlist.GroupBy(n => n).Where(n => n.Count() == g).Select(n => n.Key);
GroupBy is the best choice in this case,anyway this is an alternative:
mainlist.Where(x => mainlist.Count(i => x == i) == g).ToList();
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
LINQ Partition List into Lists of 8 members
how do I chunk an enumerable?
I have a list of many items, and a Method that works well on shorter lists of those same items.
Can I use LINQ to pull off N elements from the big list, and pass them into the Method, N at a time? I'm sure there is an elegant way to to this without having to make an "int i=0;" variable.
Let me be clear, I know that foo.Take(10) will get me 10 items off the list. But I need to keep processing the next set of 10, then the next set of 10 and so on. The pseudo code should be something like:
var shortList = BigList.NiceMethod(10);
foreach (shorty in shortlist)
{
Method(shorty);
}
This is probably some Group call.
This will give you a list of Lists where every List has at most N elements.
int N = 3;
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var ListOfLists = list.Select((x, inx) => new { Item = x, Group = inx / N })
.GroupBy(g => g.Group, g => g.Item)
.Select(x => x.ToList())
.ToList();
You can also use Morelinq's Batch method
var ListOfLists2 = list.Batch(3).Select(x => x.ToList()).ToList();
You can pass an IEnumerable<T> to your method and use Enumerable.Take.
var part = items.Take(10);
method(part);
For the next part you could use Skip+Take:
var part = items.Skip(10).Take(10);
method(part);
Another option: use Enumerable.GroupBy with the remainder operator % to n packets:
int groupCount = 5;
var parts = items.GroupBy(i => i % groupCount);
foreach (var p in parts)
method(p);
Edit: If you need to partition a sequence into smaller ones with the same size you can use this extension:
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> collection, int batchSize)
{
List<T> nextbatch = new List<T>(batchSize);
foreach (T item in collection)
{
nextbatch.Add(item);
if (nextbatch.Count == batchSize)
{
yield return nextbatch;
nextbatch = new List<T>(batchSize);
}
}
if (nextbatch.Count > 0)
yield return nextbatch;
}
This works for me
var l = Enumerable.Range(0, 1000).ToList<int>();
int size = 11;
var result = Enumerable.Range(0, l.Count / size + 1)
.Select(p => l.Skip(p * size).Take(Math.Min(size, l.Count - size * p)).ToList())
.Where(p=>p.Count > 0).ToList();