LINQ用于区分集合

时间:2009-03-09 03:53:51

标签: c# linq

我有以下数组:

var original= new int[] { 2, 1, 3 };
var target = new int[] { 1, 3, 4 };
enum Operation {Added,Removed}

我想执行一个返回以下内容的LINQ查询:

{{2,Removed},{4,Added}}

限制:我希望LINQ能够非常有效地执行此操作并避免使用O(n ^ 2)样式算法。

4 个答案:

答案 0 :(得分:3)

在这种情况下,LINQ解决方案可能不是最佳选择。

这将生成一个包含所需结果的字典。

Dictionary<int, Operation> difference = new Dictionary<int,Operation>();
foreach (int value in original) {
    difference.Add(value, Operation.Removed);
}
foreach (int value in target) {
    if (difference.ContainsKey(value)) {
        difference.Remove(value);
    } else {
        difference.Add(value, Operation.Added);
    }
}

为了保持字典的大小,也许可以将字符串循环。我来看看......

编辑:
这是:

Dictionary<int, Operation> difference = new Dictionary<int,Operation>();
IEnumerator<int> o = ((IEnumerable<int>)original).GetEnumerator();
IEnumerator<int> t = ((IEnumerable<int>)target).GetEnumerator();
bool oActive=true, tActive=true;
while (oActive || tActive) {
    if (oActive && (oActive = o.MoveNext())) {
        if (difference.ContainsKey(o.Current)) {
            difference.Remove(o.Current);
        } else {
            difference.Add(o.Current, Operation.Removed);
        }
    }
    if (tActive && (tActive = t.MoveNext())) {
        if (difference.ContainsKey(t.Current)) {
            difference.Remove(t.Current);
        } else {
            difference.Add(t.Current, Operation.Added);
        }
    }
}

EDIT2:
我做了一些性能测试。第一个版本的运行速度提高了10%-20%,包括排序列表和随机排序列表。

我列出了1到100000的数字,随机跳过了10%的数字。在我的机器上,代码的第一个版本在大约16毫秒内匹配列表。

答案 1 :(得分:1)

enum Operation { Added, Removed, }

static void Main(string[] args)
{
    var original = new int[] { 2, 1, 3 };
    var target = new int[] { 1, 3, 4 };

    var result = original.Except(target)
        .Select(i => new { Value = i, Operation = Operation.Removed, })
        .Concat(
            target.Except(original)
            .Select(i => new { Value = i, Operation = Operation.Added, })
            );

    foreach (var item in result)
        Console.WriteLine("{0}, {1}", item.Value, item.Operation);
}

我不认为你可以使用LINQ扩展方法只使用一次通过LINQ,但可能能够编写自定义扩展方法。您的权衡可能是延期执行的损失。比较两者的相对表现会很有意思。

答案 2 :(得分:0)

你运气不好。如果您在评论中说明列表未排序,则无法计算您在单个前向传递中寻找的差异。考虑:

{ 1, 2, 3, 4, 5, 6, 7, ...
{ 1, 2, 3, 6, 7, 8, 9, ...

在遇到的第一个差异(4对6)时,您无法确定是否正在查看4&amp;的删除。 5(如果两个列表单调增加,或者插入分别为6,7,8和9),如果列表继续这样的情况就会出现这种情况:

{ 1, 2, 3,             4, 5, 6, 7, 8, 9,...
{ 1, 2, 3, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9,...

答案 3 :(得分:0)

这将在单次传递中实现结果,但是我不确定GroupBy操作的复杂性。

var original= new int[] { 1, 2, 3 };
var target = new int[] { 1, 3, 4 };

var output = original.Select( i => new { I = i, L = "o" } )
    .Concat( target.Select( i => new { I = i, L = "t" } ) )
    .GroupBy( i => i.I ).Where( i => i.Count() == 1 )
.Select( i => new { I = i.Key, S = (i.ElementAt( 0 ).L == "o" ? Operation.Removed : Operation.Added) } );