使用 LINQ 在键索引上连接多维数组

发布于 2024-11-07 14:30:36 字数 1310 浏览 0 评论 0原文

我有 N 个多维源数据数组,每个数组具有相同的列数(本例中 C=4),但行数任意:

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[,] { ... };

我还有一个数组,指定源数组中的哪些索引将用作连接键:

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

我需要以生成的数组如下所示的方式连接数组:

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 }
};

我想我需要从每个源数组中选择不同的键并循环遍历所有数据并比较每一行等以填充结果数组。然而,应该有一种更有效的方法来使用 LINQ 来做到这一点 - 有人可以帮忙吗?

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 from 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

始终不够 2024-11-14 14:30:36

如果您使用 double[][] 而不是 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();

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();
吃兔兔 2024-11-14 14:30:36

我有一个解决方案给你。它可能不像您想要的那么干净,但它会起作用。这需要您更改数组的使用:

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

as .NET 将第一个视为单个 IEnumerable 而不是 IEnumerable>。另外,为了支持 null,您必须使用 nullable-double。以下代码确实期望所有锯齿状数组具有相同的大小。

接下来,您必须定义一个类来保存动态键并进行比较:

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;
    }
}

然后您可以使用以下逻辑来执行查询并返回您期望的双精度?[][]:

// 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();

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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文