我有 3 個帶有通用 ID 的串列。我需要在一個串列中按物件分組,并從其他兩個中提取資料。將舉例以更好地理解
groupNames 表:
| Id | Name |
|--------------|
| 1 | Hello |
| 2 | Hello |
| 3 | Hey |
| 4 | Dude |
| 5 | Dude |
countId 表:
| Id | whatever |
|---------------|
| 1 | test0 |
| 1 | test1 |
| 2 | test2 |
| 3 | test3 |
| 3 | test4 |
上次表:
| Id | timestamp |
|-----------------|
| 1 | 1636585230 |
| 1 | 1636585250 |
| 2 | 1636585240 |
| 3 | 1636585231 |
| 3 | 1636585230 |
| 5 | 1636585330 |
我期待像這樣的串列
| Name | whateverCnt | lastTimestamp |
|---------------------------------------|
| Hello | 3 | 1636585250 |
| Hey | 2 | 1636585231 |
| Dude | 0 | 1636585330 |
現在我有這樣的東西,但它不起作用
return groupNames
.GroupBy(x => x.Name)
.Select(x =>
{
return new myElem
{
Name = x.Name,
lastTimestamp = new DateTimeOffset(lastTime.Where(a => groupNames.Where(d => d.Name == x.Key).Select(d => d.Id).Contains(a.Id)).Max(m => m.timestamp)).ToUnixTimeMilliseconds(),
whateverCnt = countId.Where(q => (groupNames.Where(d => d.Name == x.Key).Select(d => d.Id)).ToList().Contains(q.Id)).Count()
};
})
.ToList();
非常感謝您的任何建議。
uj5u.com熱心網友回復:
在您的示例中,最安全的是最后一個指定物件的串列,并且只需 LINQ 查詢其他物件陣列以獲得相同的 id。
所以像
public IEnumerable<SomeObject> MergeListsById(
IEnumerable<GroupNames> groupNames,
IEnumerable<CountId> countIds,
IEnumerable<LastTime> lastTimes)
{
IEnumerable<SomeObject> mergedList = new List<SomeObject>();
groupNames.ForEach(gn => {
mergedList.Add(new SomeObject {
Name = gn.Name,
whateverCnt = countIds.FirstOrDefault(ci => ci.Id == gn.Id)?.whatever,
lastTimeStamp = lastTimes.LastOrDefault(lt => lt.Id == gn.Id)?.timestamp
});
});
return mergedList;
}
在 Fiddle 或一次性專案中嘗試并根據您的需要進行調整。出于可讀性和可維護性的考慮,這里可能不需要純 LINQ 中的解決方案。
是的,正如評論所說,請仔細考慮 LINQ 是否是您的最佳選擇。雖然它有效,但它的性能并不總是比“簡單”的 foreach 更好。LINQ 的主要賣點始終是保持可讀性的簡短的單行查詢陳述句。
uj5u.com熱心網友回復:
我想我會為此跳過 LINQ
class Thing{
public string Name {get;set;}
public int Count {get;set;}
public long LastTimestamp {get;set;}
}
...
var ids = new Dictionary<int, string>();
var result = new Dictionary<string, Thing>();
foreach(var g in groupNames) {
ids[g.Id] = g.Name;
result[g.Name] = new Whatever { Name = n };
}
foreach(var c in counts)
result[ids[c.Id]].Count ;
foreach(var l in lastTime){
var t = result[ids[l.Id]];
if(t.LastTimeStamp < l.Timestamp) t.LastTimeStamp = l.TimeStamp;
}
我們開始制作兩個字典(你可以 ToDictionary 這個)。如果 groupNames 已經是一個映射 id:name 的字典,那么你可以跳過制作ids字典而直接使用 groupNames。這讓我們可以快速從 ID 到 Name 進行查找,但我們實際上想將結果收集到一個 name:something 映射中,所以我們也制作了其中之一。做result[name] = thing總是成功的,即使我們以前見過name。如果需要,我們可以在此處使用 ContainsKey 檢查跳過一些物件創建
然后我們需要做的就是列舉我們的其他 N 個集合,構建結果。我們想要的結果是從中訪問的,result[ids[some_id_value_here]]并且如果 groupnames id 空間完整,它總是存在(我們永遠不會在 groupNames 中沒有的計數中擁有 id)
對于計數,我們不關心任何其他資料;僅 id 的存在就足以增加計數
對于日期,這是一個簡單的最大值演算法,“如果已知最大值小于新最大值,則已知最大值 = 新最大值”。如果您知道您的日期串列是按升序排列的,您也可以跳過它。
uj5u.com熱心網友回復:
嗯,有
List<(int id, string name)> groupNames = new List<(int id, string name)>() {
( 1, "Hello"),
( 2, "Hello"),
( 3, "Hey"),
( 4, "Dude"),
( 5, "Dude"),
};
List<(int id, string comments)> countId = new List<(int id, string comments)>() {
( 1 , "test0"),
( 1 , "test1"),
( 2 , "test2"),
( 3 , "test3"),
( 3 , "test4"),
};
List<(int id, int time)> lastTime = new List<(int id, int time)>() {
( 1 , 1636585230 ),
( 1 , 1636585250 ),
( 2 , 1636585240 ),
( 3 , 1636585231 ),
( 3 , 1636585230 ),
( 5 , 1636585330 ),
};
從技術上講,您可以使用以下Linq:
var result = groupNames
.GroupBy(item => item.name, item => item.id)
.Select(group => (Name : group.Key,
whateverCnt : group
.Sum(id => countId.Count(item => item.id == id)),
lastTimestamp : lastTime
.Where(item => group.Any(g => g == item.id))
.Max(item => item.time)));
我們來看一下:
Console.Write(string.Join(Environment.NewLine, result));
結果:
(Hello, 3, 1636585250)
(Hey, 2, 1636585231)
(Dude, 0, 1636585330)
但要小心:(List<T>我的意思是countId和lastTime)在這里不是有效的資料結構。在 Linq 查詢中,我們必須掃描它們才能獲得Sum和Max。如果countId和lastTime是長的,把它們(由分組)轉換成Dictionary<int, T>與id被Key
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/382383.html
