提问者:小点点

如何通过id合并多个列表并获得特定数据?


我有3个常用ID的列表。我需要在一个列表中按对象分组,并从另外两个列表中提取数据。将给出更多理解的示例

组名表:

| 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    |

表为lastTime:

| 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();

非常感谢任何建议。


共3个答案

匿名用户

在您的示例中,最安全的是最后一个指定对象的列表,只需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;
}

在小提琴或一次性项目中尝试一下,并根据您的需求进行调整。为了可读性和可运维性,这里可能不需要纯LINQ中的解决方案。

是的,正如评论所说,请仔细考虑LINQ是否是您的最佳选择。虽然它有效,但在性能上并不总是比“简单”的Foreach更好。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;
}

我们开始制作两本词典(你可以把这本词典翻成字典)。。如果groupNames已经是映射id:name的字典,那么您可以跳过制作ids字典,直接使用groupNames。这使我们能够快速地从ID到名称进行查找,但实际上我们希望将结果合并到名称:something映射中,所以我们也制作了其中的一个。做result[name]=thing总是成功的,即使我们以前见过name。如果您愿意,我们可以在这里跳过ContainsKey检查来创建一些对象

对于计数,我们不关心任何其他数据;只要存在id就足以增加计数

对于日期,它是一个简单的最大算法,“如果已知的最大值小于新的最大值,就知道最大值=新的最大值”。如果你知道你的日期列表是按升序排序的,你也可以跳过它...

匿名用户

  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