使用动态列数连接DataTables的功能方法

时间:2014-03-13 18:08:29

标签: c# linq datatable functional-programming

我有一个项目,允许用户通过左边连接到DataTable(通过帮助方法转换为List<T>)将列添加到DataTable。目前,我已经实现了这项工作:

public static DataTable ListToDataTable<T>(this IList<T> data)
        {
            DataTable dt = new DataTable();
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
            for (int i = 0; i < props.Count; i++)
            {
                PropertyDescriptor prop = props[i];
                dt.Columns.Add(prop.Name, prop.PropertyType);
            }
            object[] values = new object[props.Count];
            foreach (T t in data)
            {
                for (int i = 0; i < values.Length; i++)
                {
                    values[i] = props[i].GetValue(t);
                }
                dt.Rows.Add(values);
            }
            return dt;
        }
        //for purposes of this demo, the first column in leftTable is always the 
        //column that's joined on
        public static DataTable JoinWithList<T>(this DataTable leftTable, IList<T> data,string propertyToAdd)
        {
            var rightTable = new DataTable();
            rightTable = data.ListToDataTable();
            var joiningColumn = leftTable.Columns[0].ColumnName;
            var columnIndex = 0;
            //find the index of type T whose property is the same as leftTable's

            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
            for (int i = 0; i < props.Count; i++)
            {
                PropertyDescriptor prop = props[i];
                if (prop.Name ==joiningColumn)
                {
                    columnIndex = i;
                }
            }
            var results = (from u in leftTable.AsEnumerable()
                          join s in rightTable.AsEnumerable()
                          on u[0] equals s[columnIndex] into ps
                          from p in ps.DefaultIfEmpty()
                          select new
                          {
                              MembershipId = u[0],
                              UserName = u[1],
                              //some sort of function to iterate over and add columns here
                              Salary = p == null ? 0.01 : p[propertyToAdd] = p[propertyToAdd]
                          }).ToList().ListToDataTable();

            return results;
        }

我希望能够传入一个List<string>作为最后一个参数,并且select new块内部有一个可变数量的列,这些列被添加到新的匿名类型中。

1 个答案:

答案 0 :(得分:1)

从功能上思考。 尝试隔离基本案例并提供可以直接翻译成您选择的函数语言的递归定义。

让我们看看{ML} F#中join的定义是什么:

_ join [] = []
[] join _ = []
H::T join L =
    append
        (L |> choose (match H) |> map (e -> (H, e)))
        (T join L)

您需要正确构建match函数,比较正确的列,以及传递给map的函数来构建新行(在我的示例中只是一个元组)。

在C#中(不太优雅,效率低但希望在填写MatchCreate后完成),通用版本可能如下所示:

    public bool Match<A, B>(A a, B b)
    {
        // Match code
        return true;
    }

    public C Create<A, B, C>(A a, B b)
    {
        // Create new record
        return default(C);
    }

    public IList<C> Join<A, B, C>(IList<A> a, IList<B> b)
    {
        if(!a.Any() || !b.Any()) return new List<C>();
        var aHead = a[0];
        var bMatches = b.Where(bEl => Match(aHead, bEl));
        var newCs = bMatches.Select(bEl => Create<A, B, C>(aHead, bEl)).ToList();
        newCs.AddRange(Join<A, B, C>(a.Skip(1).ToList(), b));  // Recursive call
        return newCs;
    }