LINQ中的完整外部联接针对唯一密钥进行了优化

时间:2018-09-06 14:54:20

标签: c# performance linq

假定这两个有序序列(元素是唯一的):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:axsl="http://www.w3.org/1999/XSL/Transform-Alias"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:param name="pattern1" as="xs:string" select="'foo/bar'"/>

  <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>

  <xsl:variable name="stylesheet">
      <axsl:stylesheet version="3.0">
          <axsl:mode on-no-match="shallow-copy"/>
          <axsl:template match="{$pattern1}"/>
      </axsl:stylesheet>
  </xsl:variable>

  <xsl:template match="/">
      <xsl:sequence select="transform(
                              map {
                                 'source-node' : .,
                                 'stylesheet-node' : $stylesheet
                              }
                            )?output"/>
  </xsl:template>

</xsl:stylesheet>

所需结果是完全外部联接:

var outer = new char[] { 'a', 'b', 'c', 'other' };
var inner = new char[] { 'a', 'b', 'c', 'altro' };

完全外部联接可以通过LINQ来实现(这是SO的答案):LINQ - Full Outer Join

但是此解决方案并未考虑到两个序列中的元素都是唯一。如何优化算法?

1 个答案:

答案 0 :(得分:1)

基于referenced answer,我尝试了一些不会创建Lookup而是创建Dictionary的事情。

恕我直言,这是您唯一可以削减的方法,实际上可以节省一些时间。

跳过HashSet的创建(如我的急切评论中所提议的)不是一种选择,因为这将导致所有联接对的重复。

public static class Extensions {
    public static IEnumerable<TResult> FullOuterJoin<TA, TB, TKey, TResult>(
        this IEnumerable<TA> a,
        IEnumerable<TB> b,
        Func<TA, TKey> selectKeyA,
        Func<TB, TKey> selectKeyB,
        Func<TA, TB, TKey, TResult> projection,
        TA defaultA = default(TA),
        TB defaultB = default(TB),
        IEqualityComparer<TKey> cmp = null) {

        cmp = cmp ?? EqualityComparer<TKey>.Default;
        var adict = a.ToDictionary(selectKeyA, cmp);
        var bdict = b.ToDictionary(selectKeyB, cmp);

        var keys = new HashSet<TKey>(adict.Keys, cmp);
        keys.UnionWith(bdict.Keys);

        var join = from key in keys
                   let xa = adict.GetOrDefault(key, defaultA)
                   let xb = bdict.GetOrDefault(key, defaultB)
                   select projection(xa, xb, key);

        return join;
    }

    public static T GetOrDefault<K, T>(this IDictionary<K, T> d, K k, T def = default(T)) 
        => d.TryGetValue(k, out T value) ? value : def;
}