如何仅使用数据连接唯一列

时间:2012-02-10 23:44:55

标签: c# linq .net-4.0 datatable

我在下面使用这个:

    public static DataTable DataTableJoiner(DataTable dt1, DataTable dt2)
    {
        using (DataTable targetTable = dt1.Clone())
        {
            var dt2Query = dt2.Columns.OfType<DataColumn>().Select(dc =>
                new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, 
                    dc.ColumnMapping));
            var dt2FilterQuery = from dc in dt2Query.AsEnumerable()
                                 where targetTable.Columns
                                       .Contains(dc.ColumnName) == false
                                 select dc;
            targetTable.Columns.AddRange(dt2FilterQuery.ToArray());
            var rowData = from row1 in dt1.AsEnumerable()
                          join row2 in dt2.AsEnumerable()
                          on row1.Field<int>("Code") equals 
                             row2.Field<int>("Code")
                          select row1.ItemArray
                              .Concat(row2.ItemArray
                              .Where(r2 => 
                                  row1.ItemArray.Contains(r2) == false)).ToArray();
            foreach (object[] values in rowData) targetTable.Rows.Add(values);
            return targetTable;
        }
    }

此行存在问题:

select row1.ItemArray.Concat(row2.ItemArray.Where(r2 => 
     row1.ItemArray.Contains(r2) == false)).ToArray();

似乎是说如果这个值(而不是列)已经存在,请不要包括我。

我正在使用此方法根据两个表共享的列将两个表连接在一起,但我只希望将具有两个表数据的唯一列作为最终结果。

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

我不确定我是否理解你的要求100%,但是这个:

row2.ItemArray.Where(r2 => row1.ItemArray.Contains(r2) == false)

将过滤掉恰好出现在表1的任何列中的项目,而不仅仅是您要加入的列。

所以我尝试做的是使用Where扩展方法的重载来基于索引过滤项目:

// Get the index of the column we are joining on:
int joinColumnIndex = dt2.Columns.IndexOf("Code");

// Now we can filter out the proper item in the rowData query:
row2.ItemArray.Where((r2,idx) => idx != joinColumnIndex)

...

不,等等。此处:

var dt2FilterQuery = from dc in dt2Query.AsEnumerable()
                     where targetTable.Columns
                           .Contains(dc.ColumnName) == false
                     select dc;

您正在过滤掉表2中列出的所有列,其名称也出现在表1中。所以您可能想要的是:

public static DataTable DataTableJoiner(DataTable dt1, DataTable dt2)
{
    DataTable targetTable = dt1.Clone();

    var dt2Query = dt2.Columns.OfType<DataColumn>().Select(dc =>
        new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, 
            dc.ColumnMapping));

    var dt2FilterQuery = from dc in dt2Query.AsEnumerable()
                         where !targetTable.Columns.Contains(dc.ColumnName)
                         select dc;

    var columnsToAdd = dt2FilterQuery.ToArray();
    var columnsIndices = columnsToAdd.Select(dc => dt2.Columns.IndexOf(dc.ColumnName));

    targetTable.Columns.AddRange(columnsToAdd);

    var rowData = from row1 in dt1.AsEnumerable()
                      join row2 in dt2.AsEnumerable()
                      on row1.Field<int>("Code") equals 
                         row2.Field<int>("Code")
                      select row1.ItemArray
                          .Concat(row2.ItemArray
                          .Where((r2,idx) => 
                              columnsIndices.Contains(idx))).ToArray();

    foreach (object[] values in rowData) targetTable.Rows.Add(values);
    return targetTable;
}

顺便说一下。我不太明白为什么要在using语句中包装返回的DataTable。 Imho将你返回给你的呼叫者的对象处理起来是毫无意义的......