0

我有一个对象Object1和这个对象的两个列表:

public class Object1
{
    public TimeSpan? Time { get; set; }
    public List<string> Parameters { get; set; }
}

List<Object1> List1 = List<Object1>();
List<Object1> List2 = List<Object1>();

现在,我想合并这两个列表,但这应该发生在Parameters对象内的列表上。(此列表中的每个字符串组合只能出现一次。)如果ParametersList1 和 List2 中都存在相同的字符串,则 List1中的字符串Time应该被 List2 中的字符串覆盖。

例如:

List1:               List2:
 Parameters   Time    Parameters   Time
 1;1          null    1;1          1:20
 1;2          null    1;2          0:51
 1;3          null
 2;5          0:30

Result:
 Parameters   Time
 1;1          1:20
 1;2          0:51
 1;3          null
 2;5          0:30

这可能可以通过使用 for 循环来实现,但由于列表可能会变得非常大,因此它的性能可能不会那么好。

我已经尝试使用以下代码来执行此操作,但这似乎只是连接列表。

var query = List1.Concat(List2)
 .GroupBy(x => x.Parameters)
 .Select(g => g.OrderByDescending(x => x.Time).First())
 .ToList();
4

2 回答 2

2

我的解决方案使用 LINQ 和自定义的 Comparer 实例来比较 Parameters 成员,并对 Parameters 中的项目做出一些假设。但是,如果这些假设是正确的,您可以通过使用像Max's answer中建议的 Param 属性来简化实现。这是它的要点:

private static List<Object1> MergeLists(List<Object1> list1, List<Object1> list2)
{
    var parameterComparer = new ParameterComparer();

    var distinctParameters = list1.Select(o => o.Parameters)
        .Concat(list2.Select(o => o.Parameters))
        .Distinct(parameterComparer);

    return (from p in distinctParameters
            let o1 = list1.SingleOrDefault(o => parameterComparer.Equals(p, o.Parameters))
            let o2 = list2.SingleOrDefault(o => parameterComparer.Equals(p, o.Parameters))
            let result = o2 ?? o1
            select result).ToList();
}

这是一个更完整的测试驱动答案。首先,Object1 声明——我添加了一个辅助构造函数以使声明更简洁:

public class Object1
{
    public TimeSpan? Time { get; set; }
    public List<string> Parameters { get; set; }

    public Object1(TimeSpan? time, params string[] parameters)
    {
        Time = time;
        Parameters = parameters.ToList();
    }
}

接下来是测试方法。我定义了 Object1Comparer 以使测试的实现更简单——解决方案不需要它。

[TestMethod]
public void MergeListsTest()
{
    // Arrange
    var list1 = new List<Object1>
                    {
                        new Object1(null, "1", "1"),
                        new Object1(null, "1", "2"),
                        new Object1(null, "1", "3"),
                        new Object1(new TimeSpan(0, 0, 30), "2", "5")
                    };
    var list2 = new List<Object1>
                    {
                        new Object1(new TimeSpan(0, 1, 20), "1", "1"),
                        new Object1(new TimeSpan(0, 0, 51), "1", "2"),
                    };
    var expected = new List<Object1>
                        {
                            new Object1(new TimeSpan(0, 1, 20), "1", "1"),
                            new Object1(new TimeSpan(0, 0, 51), "1", "2"),
                            new Object1(null, "1", "3"),
                            new Object1(new TimeSpan(0, 0, 30), "2", "5")
                        };

    // Act
    List<Object1> actual = MergeLists(list1, list2);

    // Assert
    // Note: need to order the actual result to use CollectionAssert.AreEqual()
    List<Object1> orderedActual = actual.OrderBy(o => string.Join(";", o.Parameters)).ToList();
    CollectionAssert.AreEqual(expected, orderedActual, new Object1Comparer());
}

public class Object1Comparer : IComparer, IComparer<Object1>
{
    public int Compare(Object1 x, Object1 y)
    {
        if (x.Time == null && y.Time == null) return 0;
        if (x.Time == null || y.Time == null) return -1;
        int timeComparison = TimeSpan.Compare(x.Time.Value, y.Time.Value);
        if (timeComparison != 0) return timeComparison;

        if (x.Parameters == null && y.Parameters == null) return 0;
        if (x.Parameters == null || y.Parameters == null) return -1;
        if (x.Parameters.SequenceEqual(y.Parameters)) return 0;
        return -1;
    }

    public int Compare(object x, object y)
    {
        if (x is Object1 && y is Object1)
            return Compare(x as Object1, y as Object1);
        return -1;
    }
}

最后,这是 MergeLists 的实现:

public class ParameterComparer : IEqualityComparer<List<string>>
{
    public bool Equals(List<string> x, List<string> y)
    {
        if (x == null && y == null) return true;
        if (x == null || y == null) return false;

        return x.SequenceEqual(y);
    }

    public int GetHashCode(List<string> obj)
    {
        if (obj == null) throw new ArgumentNullException("obj");

        // Note: this is not a safe way to get a hash code,
        // but if you're sure that the members are always ordered
        // and will never contain a semi-colon, then it will work.
        return string.Join(";", obj).GetHashCode();
    }
}

private static List<Object1> MergeLists(List<Object1> list1, List<Object1> list2)
{
    var parameterComparer = new ParameterComparer();

    var distinctParameters = list1.Select(o => o.Parameters)
        .Concat(list2.Select(o => o.Parameters))
        .Distinct(parameterComparer);

    return (from p in distinctParameters
            let o1 = list1.SingleOrDefault(o => parameterComparer.Equals(p, o.Parameters))
            let o2 = list2.SingleOrDefault(o => parameterComparer.Equals(p, o.Parameters))
            let result = o2 ?? o1
            select result).ToList();
}
于 2013-03-22T17:19:35.080 回答
1

像这样修改你的 Object1

public class Object1
{
    public Object1()
    {
        this.Parameters = new List<string>();
    }

    public TimeSpan? Time { get; set; }

    public List<string> Parameters { get; set; }

    public string Param
    {
        get
        {
            return string.Join(",", this.Parameters.OrderBy(o => o).ToArray());
        }

    }
}

像这样修改您的查询

var query = list1.Concat(list2)
             .GroupBy(x => x.Param) // <- Changed with the new properties
             .Select(g => g.OrderByDescending(x => x.Time).First())
             .ToList();

最大限度

于 2013-03-22T16:57:22.447 回答