我有一個List<TimeAndCode>物件。每個物件都包含一個Code和一個TimeSpan值。
public struct TimeAndCode {
public TimeSpan Time { get; set; }
public string Code { get; set; }
}
出現的代碼預先定義為"CO"和。"GO""BT"
當Time屬性達到相同的值時,串列的順序需要采用特定格式。例如(用于可讀性的 JSON 表示)
[
{"Time" : "08:00:00", "Code" : "CO" },
{"Time" : "09:00:00", "Code" : "GO" },
{"Time" : "09:30:00", "Code" : "CO" },
{"Time" : "09:30:00", "Code" : "GO" },
{"Time" : "09:30:00", "Code" : "CO" },
{"Time" : "09:30:00", "Code" : "GO" },
{"Time" : "09:30:00", "Code" : "BT" },
{"Time" : "12:30:00", "Code" : "CO" },
{"Time" : "12:30:00", "Code" : "GO" },
{"Time" : "13:30:00", "Code" : "CO" },
{"Time" : "13:30:00", "Code" : "GO" },
{"Time" : "13:30:00", "Code" : "BT" },
{"Time" : "13:30:00", "Code" : "CO" },
{"Time" : "13:30:00", "Code" : "GO" }
]
所以模式是CO -> GOor GO -> BTor CO -> GO -> BT -> CO -> GO。
可能有一個很好的舊if else解決方案,但我正在尋找一個很好且易于使用的 LINQ 解決方案。(例如list.OrderBy( x => x.Time ).RearrangeBy( x => x.Code, pattern );)
編輯
只有在代碼位于同一時間段時,才能進行自定義訂購。
最簡單的模式是:
CO -> GO -> CO -> GO -> CO -> GO
但也有可能存在 BT:
CO -> GO -> BT -> CO -> GO -> BT -> CO -> GO -> BT -> CO
但也有可能只有一個BT:
CO -> GO -> BT -> GO -> GO -> CO -> GO -> CO -> GO
CO -> GO -> CO -> GO -> BT -> CO -> GO -> CO -> GO
CO -> GO -> CO -> GO -> CO -> GO -> BT -> CO -> GO
CO -> GO -> BT -> CO -> GO -> CO -> GO -> BT -> CO -> GO
所以主要模式是CO -> GO -> BT。
編輯 2 已解決
I created an extension method. May be this code is useful for somebody. (room for improvement)
public static IEnumerable<TSource> RearrangeByPattern<TSource, TKey>(this IEnumerable<TSource> list, Func<TSource, TKey> keySelector, IEnumerable<TKey> pattern)
{
var groups = list.GroupBy(keySelector).OrderBy(x => pattern.IndexOf(x.Key));
var maxOccurences = groups.Select(x => x.Count()).Max();
var result = new List<TSource>();
for (var i = 0; i < maxOccurences; i )
{
foreach (var group in groups)
{
if (group.Count() > i)
{
result.Add(group.ElementAt(i));
}
}
}
return result;
}
Now I can use it like this:
var list = new List<TimeAndCode>();
... // add values
var ordered = list.OrderBy(x => x.Date).RearrangeByPattern( x => x.Code, new string[] { "CO", "GO", "BT" });
uj5u.com熱心網友回復:
這是一個建議 where TimeAndCode.Codeis anenum而不是 a string。
它將始終以重復的CO, GO,BT模式對具有相同時間跨度的條目進行排序;這意味著如果例如五個具有相同時間跨度的條目具有以下Code選擇:2 x CO, 1 x BT, 2 x GO,它將始終將它們排序為CO, GO, BT, CO, GO(而不是CO, GO, CO, GO, BT)。
我通過OrderBy基于時間跨度、索引(在嵌套組內生成)和Code每個條目的數值生成屬性來實作這一點。
使用以下型別:
public struct TimeAndCode
{
public TimeSpan Time { get; set; }
public Code Code { get; set; }
}
public enum Code
{
Undefined,
CO,
GO,
BT
}
我們可以寫出以下運算式:
List<TimeAndCode> result = list
.GroupBy(entry => entry.Code)
.SelectMany(gr => gr
.GroupBy(entry => entry.Time)
.SelectMany(gr => gr.Select((entry, index) => (
OrderBy: entry.Time.ToString() index (int)entry.Code,
TimeAndCode: entry))))
.OrderBy(entry => entry.OrderBy)
.Select(entry => entry.TimeAndCode)
.ToList();
哪里list是List<TimeAndCode>。
使用示例輸入如下:
List<TimeAndCode> list = new List<TimeAndCode>
{
new TimeAndCode { Time = new TimeSpan(09, 00, 00), Code = Code.GO },
new TimeAndCode { Time = new TimeSpan(13, 30, 00), Code = Code.BT },
new TimeAndCode { Time = new TimeSpan(09, 30, 00), Code = Code.GO },
new TimeAndCode { Time = new TimeSpan(09, 30, 00), Code = Code.GO },
new TimeAndCode { Time = new TimeSpan(13, 30, 00), Code = Code.GO },
new TimeAndCode { Time = new TimeSpan(08, 00, 00), Code = Code.CO },
new TimeAndCode { Time = new TimeSpan(09, 30, 00), Code = Code.BT },
new TimeAndCode { Time = new TimeSpan(12, 30, 00), Code = Code.CO },
new TimeAndCode { Time = new TimeSpan(09, 30, 00), Code = Code.CO },
new TimeAndCode { Time = new TimeSpan(12, 30, 00), Code = Code.GO },
new TimeAndCode { Time = new TimeSpan(13, 30, 00), Code = Code.CO },
new TimeAndCode { Time = new TimeSpan(13, 30, 00), Code = Code.CO },
new TimeAndCode { Time = new TimeSpan(09, 30, 00), Code = Code.CO },
new TimeAndCode { Time = new TimeSpan(13, 30, 00), Code = Code.GO },
};
, 應用 Linq 運算式后,我們可以列印結果
foreach (var entry in result)
{
Console.WriteLine("Time: " entry.Time " Code: " entry.Code);
}
并獲得以下輸出:
Time: 08:00:00 Code: CO
Time: 09:00:00 Code: GO
Time: 09:30:00 Code: CO
Time: 09:30:00 Code: GO
Time: 09:30:00 Code: BT
Time: 09:30:00 Code: CO
Time: 09:30:00 Code: GO
Time: 12:30:00 Code: CO
Time: 12:30:00 Code: GO
Time: 13:30:00 Code: CO
Time: 13:30:00 Code: GO
Time: 13:30:00 Code: BT
Time: 13:30:00 Code: CO
Time: 13:30:00 Code: GO
uj5u.com熱心網友回復:
這是 Astrid 的變體:
var r = list.GroupBy(tc => tc)
.SelectMany(g => g.Select((tc, i) => (tc, i)))
.OrderBy(t => (t.tc.Time, t.i, t.tc.Code))
.Select(t => t.tc);
使用以下前體設定:
public record TimeAndCode(TimeSpan Time, Code Code);
public enum Code { CO, GO, BT }
...
var list = new List<TimeAndCode>
{
new (TimeSpan.FromHours(8), Code.CO),
new (TimeSpan.FromHours(9), Code.GO),
new (TimeSpan.FromHours(9.5), Code.CO),
new (TimeSpan.FromHours(9.5), Code.GO),
new (TimeSpan.FromHours(9.5), Code.GO),
new (TimeSpan.FromHours(9.5), Code.CO),
new (TimeSpan.FromHours(9.5), Code.BT),
new (TimeSpan.FromHours(12.5), Code.CO),
new (TimeSpan.FromHours(12.5), Code.GO),
new (TimeSpan.FromHours(13.5), Code.CO),
new (TimeSpan.FromHours(13.5), Code.BT),
new (TimeSpan.FromHours(13.5), Code.GO),
new (TimeSpan.FromHours(13.5), Code.CO),
new (TimeSpan.FromHours(13.5), Code.GO),
};
TimeAndCode 是一條記錄,這意味著它獲得了一些用于排序和比較的有用屬性。它可以簡單地分組,因為它自動等于另一個具有相同資料的 TimeAndCode
按時間和代碼分組產生一個串列串列;兩個9:30 COs 列在一個串列中。將其傳遞給Select((tc,i)意味著i第一個為 0,第二個為 1 等等。我們所做的只是將其提升為 和 的元組,tc因為i我們稍后將需要它們,并且我們 SelectMany 撤消允許的 GroupBy我們計算相同的時間/代碼
我們按另一個元組排序;元組按它們的值按順序排序,所以要按 3 個事物 a、b 和 c 排序,我們可以 OrderBy 一個元組(a, b, c)
最后剩下要做的就是選擇tc后面的時間和代碼串列,按照你想要的排序
Like Astrid's it also doesn't put the BT at the end for 9:30 - it goes in the middle, like 13:30's does.. But you haven't provided an explanation for why 9:30's BT is at the end...
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/427135.html
標籤:c# list linq sorting collections
上一篇:LINQ如何選擇一個串列中存在但另一個串列中不存在的ID
下一篇:如何在LINQ中撰寫for回圈
