【问题标题】:Amend list using linq via second list通过第二个列表使用 linq 修改列表
【发布时间】:2016-02-12 23:59:00
【问题描述】:

我有 2 个列表。第一个是包含一堆对象的主列表,每个对象都有时间戳(Datetime)、类型(int)、值(double)和标志(int)。第二个列表有类型(int)和描述(字符串)。

我的问题是我需要第一个列表在每个时间戳都有所有类型。在没有这样的对象匹配(时间戳,类型)的情况下,我需要插入一个带有(时间戳,类型,空值和空标志)的新对象。

我如何使用 Linq 做到这一点?

示例对象:

var date = DateTime.UtcNow;
var points = new List<Point> {
    new Point { ts_utc = date            , type = 300, value = 25     , flags = 0 },
    new Point { ts_utc = date            , type = 400, value = 250    , flags = 0 },
    new Point { ts_utc = date            , type = 500, value = 2500   , flags = 0 },
    new Point { ts_utc = date.AddDays(1) , type = 300, value = 26     , flags = 0 },
    //new Point { ts_utc = date.AddDays(1) , type = 400, value = 260, flags = 0 },
    new Point { ts_utc = date.AddDays(1) , type = 500, value = 2600   , flags = 0 },
    new Point { ts_utc = date.AddDays(2) , type = 300, value = 27     , flags = 0 },
    new Point { ts_utc = date.AddDays(2) , type = 400, value = 270    , flags = 0 },
    new Point { ts_utc = date.AddDays(2) , type = 500, value = 2700   , flags = 0 }
};
var desc = new List<Description> {
    new Description{ type = 300, description = "300" },
    new Description{ type = 400, description = "400" },
    new Description{ type = 500, description = "500" },
};

 var q = from p in points
         join d in desc on p.type equals d.type into joined
         from subd in joined.DefaultIfEmpty(...)
         ...

查询不完整。在上面的列表中,我将一个点作为该(时间戳、类型)组合缺失值的示例进行了注释。在那个地方,我想插入一个默认对象,其中包含现有类型的时间戳,但插入了缺少的类型。值和标志应该为空。我想 group by 可能会派上用场?

【问题讨论】:

    标签: c# linq missing-data


    【解决方案1】:

    如果我对您的理解正确,您希望第一个集合中不存在的一组时间戳和类型组合基于该集合具有一个时间戳条目,但不是第二个集合中的所有类型。

    var missing = points.GroupBy(p => p.ts_utc)
                        .SelectMany(g => desc.Where(d => g.All(p => p.type != d.type))
                                              .Select(d => new 
                                                           { 
                                                               ts = g.Key, 
                                                               type = d.type 
                                                           }));
    

    这将首先按时间戳对点进行分组,然后对于每个分组,它将从描述列表中过滤出该组中存在的类型的任何描述,只留下该类型不存在的描述时间戳。然后它从组键和“缺失”类型中选择时间戳。最后SelectMany 将使结果变平。

    然后您可以遍历结果并将条目添加到 points 集合中。

    foreach(var m in missing)
        points.Add(new Point { ts_utc = m.ts, type = m.type });
    

    【讨论】:

      【解决方案2】:

      如果我错了,请纠正我,但您想为列表 1“填补空白” - 这样它包含每个现有日期的每个现有类型的条目?

      如果是这样,我只会让所有不同的项目迭代并创建一个嵌套的 foreach 循环...

      List<DateTime> allExistingDates = points.Select(o => o.ts_utc).Distinct().ToList();
      List<int> allExistingTypes = points.Select(o => o.type).Concat(desc.Select(o => o.type)).Distinct().ToList();
      
      foreach (DateTime d in allExistingDates)
          foreach (int t in allExistingTypes)
              if (points.Any(o => o.ts_utc == d && o.type == t) == false)
                  points.Add(new Point { ts_utc = d, type = t});
      

      【讨论】:

        【解决方案3】:

        我发现这可以修复我的列表,而无需求助于多个查询:

        var w = from p in points
                group p by p.ts_utc into g
                from d in desc
                select points.Where(r => r.ts_utc == g.Key && r.type == d.type).DefaultIfEmpty(new Point
                {
                    ts_utc = g.Key,
                    type = d.type
                }).FirstOrDefault();
        

        我认为它不会比这更短。 (此时)其他两个答案都帮助我找到了这个解决方案。它不会创建不必要的新点,是 1 个 linq 查询,我认为(使用 1 个 linq 查询)它的效率再高不过了。

        【讨论】:

        • 这也将根据时间戳和类型“删除”重复项。
        • 是的。幸运的是,我可以非常确定不会发生重复。
        猜你喜欢
        • 2017-12-16
        • 2012-08-07
        • 1970-01-01
        • 1970-01-01
        • 2018-08-27
        • 1970-01-01
        • 2014-11-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多