开发者

Joining multidimensional arrays with LINQ on key indices

I have N multidimensional source data arrays, each with the same number of columns (C=4 in this example), but any number of rows:

var array1 = new double[,]
  {
    { 1, 2, 3, 4 },
    { 5, 6, 7, 8 },
    { 9, 10, 11, 12 }
  };

var array2 = new double[,]
  {
    { 1, 2, 5, 6 },
    { 7, 8, 9, 10 },
    { 9, 10, 11, 12 }
  };

var array3 = new double[,]
  {
    { 1, 2, 7, 8 },
    { 13, 14, 15, 16 }
  };

...
var arrayN = new double[,] { ... };

I also have an array that specifies which indices in the source arrays are to be used as the join keys:

var keyArray = new int[] { 0, 1 };

I need to join the arrays in such as way that the resulting array will look like:

var result = new double[,]
{
  // The length of each element in this array will be (C x N),
  // the first C elements will be from array1, the next C from 
  // array2, and so on, and nulls used for arrays elements that 
  // are not included in the join (keys don't match).
  //
  // The number of rows in this array will be the number of distinct key combinations.
  { 1, 2, 3, 4, 1, 2, 5, 6, 1, 2, 7, 8 },
  { 5, 6, 7, 8, null, null, null, null, null, null, null, null },
  { 9, 10, 11, 12, 9, 10, 11, 12, null, null, null, null },
  { null, null, null, null, 7, 8, 9, 10, null, null, null, null },
  { null, null, null, null, null, null, null, null, 13, 14, 15, 16 }
};

I am thinking I need to select the distinct keys fro开发者_如何学JAVAm each source array and loop through all of the data and compare each row, etc. to fill the results array. However, there should be a more efficient way to do this using LINQ - can anyone help?


Here is a solution if you use double[][] instead of double[,]

var array1 = new double[][]
            {
              new double[] {1,2,3,4},
              new double[] {5,6,7,8},
              new double[] {9,10,11,12}
            };

var array2 = new double[][]
            {
              new double[] {1,2,5,6},
              new double[] {7,8,9,10},
              new double[] {9,10,11,12}
            };

var key = new int[] { 0, 1 };

 double?[][] result = (from a in array1
                       from b in array2.Where(bi => key.Select(k => bi[k] == a[k])
                                                       .Aggregate((k1, k2) => k1 && k2))
                                       .DefaultIfEmpty()
                       select a.Select(an => (double?)an)
                               .Concat(b == null ?
                                       a.Select(an => (double?)null) :
                                       b.Select(bn => (double?)bn))
                               .ToArray()
                       ).Union
                       (from b in array2
                        from a in array1.Where(ai => key.Select(k => ai[k] == b[k])
                                                        .Aggregate((k1, k2) => k1 && k2))
                                        .DefaultIfEmpty()
                        where a == null
                        select b.Select(bn => (double?)null)
                                .Concat(b.Select(bn =>(double?)bn))
                                .ToArray()
                        ).ToArray();


I have a solution for you. It may not be as clean as you are looking for, but it will work. It would require that you change your use of arrays from:

var array1 = new double[,] to: var array1 = new double?[][]

as .NET views the first as a single IEnumerable instead of IEnumerable>. Plus to support the nulls you have to use a nullable-double. The following code does expect that all jagged arrays are of the same size.

Next you have to define a class to hold the dynamic key(s) and do the compare:

class Keys : IEquatable<Keys>
{
    private IEnumerable<double?> _keys = Enumerable.Empty<double?>();

    public override int GetHashCode()
    {
        int hash = 23;
        foreach (var element in _keys)
        {
            hash = hash * 37 + element.GetValueOrDefault().GetHashCode();
        }

        return hash;
    }

    public bool Equals(Keys other)
    {
        if (other == null)
            return false;

        if (_keys.Count() != other._keys.Count())
            return false;

        for (int index = 0; index < _keys.Count(); index++)
        {
            if (_keys.ElementAt(index) != other._keys.ElementAt(index))
                return false;
        }

        return true;
    }

    public Keys(double?[] data, int[] indexes)
    {
        var keys = new List<double?>();
        foreach (var index in indexes)
        {
            keys.Add(data[index]);
        }
        _keys = keys;
    }
}

Then you have the following logic to do the query and return an double?[][] that you would expect:

// Create full join of selection
var fullJoin = (from l in array1
                join r in array2 on new Keys(l, keyArray) equals new Keys(r, keyArray) into g
                from r in g.DefaultIfEmpty()
                select new { l, r})
               .Concat
               (from r in array2
                join l in array1 on new Keys(r, keyArray) equals new Keys(l, keyArray) into g
                from l in g.DefaultIfEmpty()
                where l == null
                select new {l, r});

// Create the final result set
var results = fullJoin.Select(i => 
{ 
    var list = new List<double?>(); 
    if (i.l != null)
    {
        list.AddRange(i.l); 
    }
    else
    {
        list.AddRange(Enumerable.Repeat((double?)null, i.r.Length));
    }
    if (i.r != null)
    {
        list.AddRange(i.r); 
    }
    else
    {
        list.AddRange(Enumerable.Repeat((double?)null, i.l.Length));
    }
    return list.ToArray();
}).ToArray();
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜