如何将查找作为2个旧查找的联合?

时间:2012-09-18 14:22:56

标签: c# .net linq

如何从2个旧查找联盟中查找?似乎a = a.Union(b)对他们不起作用。

3 个答案:

答案 0 :(得分:15)

如果您拥有查找来自的原始列表,则可能更容易。如果您使用Dictionary Lists代替查找,也可能会更容易。但是,仍然可以将两个查找对象合并为一个新对象。基本思想是从查找中检索原始值,然后从两者的连接集中创建新的查找。

var a = new[] {"apple","aardvark","barn"};
var b = new[] {"baboon", "candy", "cork"};

var al = a.ToLookup (x => x[0]);
var bl = b.ToLookup (x => x[0]);

var cl = al.Concat(bl).SelectMany(x => x).ToLookup(x => x[0]);

如果您还不知道原始键选择器功能,则可以使用此变体。

var cl = al.Concat(bl)
    .SelectMany(lookup => lookup.Select(value => new { lookup.Key, value}))
    .ToLookup(x => x.Key, x => x.value);

答案 1 :(得分:0)

可以考虑更灵活的方式来计算任何数组/列表。例如,创建同义词的查找:

public ILookup<string, string> GetSynonyms()
{
    var data = new[]
        {
            new[] {"hello", "hi there", "привет"},
            new[] {"bye", "Tchau", "Adios"},
            new[] {"hello", "hi there"}
        };

    return data
        .SelectMany(words => words.SelectMany(
            keyWord => words.Where(word => word != keyWord).Select(word => new Tuple<string, string>(item1: keyWord, item2: word))))
        .Distinct(comparer: new DelegateComparer<Tuple<string, string>>(equals: Equals, hashCode: v => v.GetHashCode()))
        .ToLookup(keySelector: k => k.Item1, elementSelector: e => e.Item2);
}

public sealed class DelegateComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> _equals;
    private readonly Func<T, int> _hashCode;

    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals = equals;
        _hashCode = hashCode;
    }

    public bool Equals(T x, T y)
    {
        return _equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        return _hashCode != null ? _hashCode(obj) : obj.GetHashCode();
    }
}

result of execution

答案 2 :(得分:0)

我写了这个扩展方法,利用了ILookup<TK,TV>IEnumberable<IGrouping<TK,TV>>

的事实。
    public static ILookup<TK, TV> Union<TK, TV>(this ILookup<TK, TV> self, IEnumerable<IGrouping<TK,TV>> moreGroupings)
    {
        return self.Concat(moreGroupings)
            .SelectMany(grouping => grouping.Select(val => new KeyValuePair<TK, TV>(grouping.Key, val)))
            .ToLookup(kvp => kvp.Key, kvp => kvp.Value);
    }

这里有一些测试证明。这里的查找包含字符串,并以其长度为键。

    [TestMethod]
    public void EmptyLookups_UnionReturnsEmpty()
    {
        var a = new string[] { }.ToLookup(x => x.Length, x => x);
        var b = new string[] { }.ToLookup(x => x.Length, x => x);
        var c = a.Union(b);
        Assert.AreEqual(0, c.Count);
        c = b.Union(a);
        Assert.AreEqual(0, c.Count);
    }

    [TestMethod]
    public void OneEmptyLookup_UnionReturnsContentsOfTheOther()
    {
        var a = new string[] { }.ToLookup(x => x.Length, x => x);
        var b = new string[] { "hello", "world" }.ToLookup(x => x.Length, x => x);
        var c = a.Union(b);
        Assert.AreEqual(1, c.Count);
        Assert.AreEqual("helloworld", string.Join("", c[5].OrderBy(x=>x)));
        c = b.Union(a);
        Assert.AreEqual(1, c.Count);
        Assert.AreEqual("helloworld", string.Join("", c[5].OrderBy(x=>x)));
    }

    [TestMethod]
    public void UniqueKeys_UnionAdds()
    {
        var a = new string[] { "cat", "frog", "elephant"}.ToLookup(x => x.Length, x => x);
        var b = new string[] { "hello", "world" }.ToLookup(x => x.Length, x => x);
        var c = a.Union(b);
        Assert.AreEqual(4, c.Count);
        Assert.AreEqual("cat", string.Join("", c[3]));
        Assert.AreEqual("frog", string.Join("", c[4]));
        Assert.AreEqual("elephant", string.Join("", c[8]));
        Assert.AreEqual("helloworld", string.Join("", c[5].OrderBy(x=>x)));
    }

    [TestMethod]
    public void OverlappingKeys_UnionMerges()
    {
        var a = new string[] { "cat", "frog", "horse", "elephant"}.ToLookup(x => x.Length, x => x);
        var b = new string[] { "hello", "world" }.ToLookup(x => x.Length, x => x);
        var c = a.Union(b);
        Assert.AreEqual(4, c.Count);
        Assert.AreEqual("cat", string.Join("", c[3]));
        Assert.AreEqual("frog", string.Join("", c[4]));
        Assert.AreEqual("elephant", string.Join("", c[8]));
        Assert.AreEqual("hellohorseworld", string.Join("", c[5].OrderBy(x=>x)));
    }

我也碰巧需要处理不区分大小写的字符串,所以我有一个需要自定义比较器的重载。

        public static ILookup<TK, TV> Union<TK, TV>(this ILookup<TK, TV> self, IEnumerable<IGrouping<TK,TV>> moreGroupings, IEqualityComparer<TK> comparer)
    {
        return self.Concat(moreGroupings)
            .SelectMany(grouping => grouping.Select(val => new KeyValuePair<TK, TV>(grouping.Key, val)))
            .ToLookup(kvp => kvp.Key, kvp => kvp.Value, comparer);
    }

这些示例中的查找使用第一个字母作为键:

    [TestMethod]
    public void OverlappingKeys_CaseInsensitiveUnionAdds()
    {
        var a = new string[] { "cat", "frog", "HORSE", "elephant"}.ToLookup(x => x.Substring(0,1), x => x);
        var b = new string[] { "hello", "world" }.ToLookup(x => x.Substring(0,1), x => x);
        var c = a.Union(b, StringComparer.InvariantCulture);
        Assert.AreEqual(6, c.Count);
        Assert.AreEqual("cat", string.Join("", c["c"]));
        Assert.AreEqual("frog", string.Join("", c["f"]));
        Assert.AreEqual("elephant", string.Join("", c["e"]));
        Assert.AreEqual("hello", string.Join("", c["h"].OrderBy(x=>x)));
        Assert.AreEqual("HORSE", string.Join("", c["H"].OrderBy(x=>x)));
        Assert.AreEqual("world", string.Join("", c["w"]));
    }

    [TestMethod]
    public void OverlappingKeys_CaseSensitiveUnionMerges()
    {
        var a = new string[] { "cat", "frog", "HORSE", "elephant"}.ToLookup(x => x.Substring(0,1), x => x);
        var b = new string[] { "hello", "world" }.ToLookup(x => x.Substring(0,1), x => x);
        var c = a.Union(b, StringComparer.InvariantCultureIgnoreCase);
        Assert.AreEqual(5, c.Count);
        Assert.AreEqual("cat", string.Join("", c["c"]));
        Assert.AreEqual("frog", string.Join("", c["f"]));
        Assert.AreEqual("elephant", string.Join("", c["e"]));
        Assert.AreEqual("helloHORSE", string.Join("", c["h"].OrderBy(x=>x)));
        Assert.AreEqual("helloHORSE", string.Join("", c["H"].OrderBy(x=>x)));
        Assert.AreEqual("world", string.Join("", c["w"]));
    }