我正在嘗試使用 C# 中的 LINQ 獲取陣列中最頻繁的值。
例如,
int[] input = {1, 1, 1, 3, 5, 5, 6, 6, 6, 7, 8, 8};
output = {1, 6}
int[] input = {1, 2, 2, 3 ,3, 3, 5}
output = {3}
請讓我知道如何構建 LINQ。
請仔細閱讀。這是使用 LINQ 選擇最頻繁值的不同問題
我必須只選擇最頻繁的值。下面的代碼類似,但是我不能使用 Take(5) 因為我不知道結果的數量。
int[] nums = new[] { 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 };
IEnumerable<int> top5 = nums
.GroupBy(i => i)
.OrderByDescending(g => g.Count())
.Take(5)
.Select(g => g.Key);
此輸出是 {1, 2, 3, 4, 5} 但我的預期輸出 = {1, 2}
請仔細閱讀問題并作答。
感謝致敬。
uj5u.com熱心網友回復:
只是為了添加過多的答案:
int[] input = { 1, 1, 1, 3, 5, 5, 6, 6, 6, 7, 8, 8 };
var result = input
.GroupBy(i => i)
.GroupBy(g => g.Count())
.OrderByDescending(g => g.Key)
.First()
.Select(g => g.Key)
.ToArray();
Console.WriteLine(string.Join(", ", result)); // Prints "1, 6"
[編輯]
如果有人覺得這很有趣,我將 .net 4.8 和 .net 5.0 之間的上述性能進行了如下比較:
(1) 添加了一個Comparer類來檢測進行的比較次數:
class Comparer : IComparer<int>
{
public int Compare(int x, int y)
{
Console.WriteLine($"Comparing {x} with {y}");
return x.CompareTo(y);
}
}
(2) 修改了OrderByDescending()對傳遞 a的呼叫Comparer:
.OrderByDescending(g => g.Key, new Comparer())
(3) 將我的測驗控制臺應用程式多定位到“net48”和“net5.0”。
進行這些更改后,輸出如下:
對于 .net 4.8:
Comparing 1 with 3
Comparing 1 with 1
Comparing 1 with 2
Comparing 3 with 3
Comparing 3 with 2
Comparing 3 with 3
1, 6
對于 .net 5.0:
Comparing 3 with 1
Comparing 3 with 2
1, 6
如您所見,.net 5.0 得到了更好的優化。然而,對于 .net Framework,(正如下面提到的 /u/mjwills),使用MaxBy()擴展來避免使用可能會更高效OrderByDescending()- 但前提是檢測表明排序導致了性能問題。
uj5u.com熱心網友回復:
如果您想在一個查詢中在純 LINQ 中執行此操作,您可以按計數對組進行分組并選擇最大一個:
int[] nums = new[] { 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 };
var tops = nums
.GroupBy(i => i)
.GroupBy(grouping => grouping.Count())
.OrderByDescending(gr => gr.Key)
.Take(1)
.SelectMany(g => g.Select(g => g.Key))
.ToList();
請注意,這不是最有效和最清晰的解決方案。
UPD
使用Aggregate執行的更有效的版本MaxBy。請注意,與前一個不同的是,對于空集合,它將失敗:
var tops = nums
.GroupBy(i => i)
.GroupBy(grouping => grouping.Count())
.Aggregate((max, curr) => curr.Key > max.Key ? curr : max)
.Select(gr => gr.Key);
您也可以使用MaxByfromMoreLinq或.NET 6 中引入的一個。
uj5u.com熱心網友回復:
您可以將結果存盤在元組的 IEnumerable 中,第一項是數字,第二項是輸入陣列中的數字計數。然后查看包含最多元素的組的計數,并獲取第二項等于最大值的所有元組。
int[] nums = new[] { 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 };
var intermediate = nums
.GroupBy(i => i)
.Select(g => (g.Key,g.Count()));
int amount = intermediate.Max(x => x.Item2);
IEnumerable<int> mostFrequent = intermediate
.Where(x => x.Item2 == amount)
.Select(x => x.Item1);
在線演示:https : //dotnetfiddle.net/YCVGam
uj5u.com熱心網友回復:
使用變數捕獲第一個專案的專案數,然后使用該變數TakeWhile獲取所有組。
void Main()
{
var input = new[] { 1, 1, 1, 3, 5, 5, 6, 6, 6, 7, 8, 8 };
int numberOfItems = 0;
var output = input
.GroupBy(i => i)
.OrderByDescending(group => group.Count());
var maxNumberOfItems = output.FirstOrDefault()?.Count() ?? 0;
var finalOutput = output.TakeWhile(group => group.Count() == maxNumberOfItems).ToList();
foreach (var item in finalOutput)
{
Console.WriteLine($"Value {item.Key} has {item.Count()} members");
}
}
您也可以將其作為單個查詢執行:
int? numberOfItems = null;
var finalOutput = input
.GroupBy(i => i)
.OrderByDescending(group => group.Count())
.TakeWhile(i =>
{
var count = i.Count();
numberOfItems ??= count;
return count == numberOfItems;
})
.ToList();
uj5u.com熱心網友回復:
您可以考慮添加擴展方法。就像是
public static IEnumerable<T> TakeWhileEqual<T, T2>(this IEnumerable<T> collection, Func<T, T2> predicate)
where T2 : IEquatable<T2>
{
using var iter = collection.GetEnumerator();
if (iter.MoveNext())
{
var first = predicate(iter.Current);
yield return iter.Current;
while (iter.MoveNext() && predicate(iter.Current).Equals(first))
{
yield return iter.Current;
}
}
}
這具有高效的優點,不需要多次迭代集合。但它確實需要更多的代碼,即使這可以隱藏在擴展方法中。
uj5u.com熱心網友回復:
您可以首先像這樣對第一個輸入進行分組。
int[] input = { 1, 1, 1, 3, 5, 5, 6, 6, 6, 7, 8, 8 };
var tmpResult = from i in input
group i by i into k
select new
{
k.Key,
count = k.Count()
};
然后你可以像這樣過濾組的最大值;
var max = tmpResult.Max(s => s.count);
在你應該做一個過濾器就足夠了
int[] result = tmpResult.Where(f => f.count == max).Select(s => s.Key).ToArray();
您也可以為此創建一個擴展方法。
public static class Extension
{
public static int[] GetMostFrequent(this int[] input)
{
var tmpResult = from i in input
group i by i into k
select new
{
k.Key,
count = k.Count()
};
var max = tmpResult.Max(s => s.count);
return tmpResult.Where(f => f.count == max).Select(s => s.Key).ToArray();
}
uj5u.com熱心網友回復:
你非常接近。只需在您的代碼中再添加一行。
int[] input = { 1, 1, 1, 3, 5, 5, 6, 6, 6, 7, 8, 8 };
var counts = input
.GroupBy(i => i)
.Select(i => new { Number = i.Key, Count = i.Count()})
.OrderByDescending(i => i.Count);
var maxCount = counts.First().Count;
var result = counts
.Where(i=> i.Count == maxCount)
.Select(i => i.Number);
結果
{1,6}
uj5u.com熱心網友回復:
我想你可能想使用 TakeWhile 而不是 Take;
int[] nums = new[] { 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 };
var n = nums
.GroupBy(i => i)
.OrderByDescending(g => g.Count());
var c = n.First().Count();
var r = n.TakeWhile(g => g.Count() == c)
.Select(g => g.Key);
如果您想一次性完成此操作,而無需 LINQ,則可以使用 Dictionary 和 List 軌道
a) 你看到一個值多少次 b) 你看到最多次數的值 c) 你看到多少次的其他大多數值
我們跳過串列,嘗試在字典中查找當前值。它要么有效,要么無效——如果有效,TryGetValue 會告訴我們當前值被看到了多少次。如果不是,TryGetValue 將使用 aseen為 0。我們增加seen。我們來看看它與我們迄今為止看到的最大值的比較:
更重要的是——我們在“最頻繁”的比賽中有了一個新的領導者——清除當前的領導者名單,并以新
n的領導者重新開始。還要注意新的最大值它是平等的 - 我們領先;
n在其同行中添加當前少 - 我們不在乎
int[] nums = new[] { 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 }; int maxSeen = int.MinValue; var seens = new Dictionary<int, int>(); var maxes = new List<int>(); foreach(var n in nums){ seens.TryGetValue(n, out var seen); seens[n] = seen; if(seen > maxSeen){ maxes = new(){n}; maxSeen = seen; } else if(seen == maxSeen) maxes.Add(n); }
你最終會得到maxes一個List<int>出現最多的數字串列。
如果您關心 List 內部陣列的分配,您可以考慮清除串列而不是newing; 我new會因為它是一個方便的一個班輪使用新的領導者的初始化程式
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/314650.html
上一篇:填充自定義型別串列中的空格
