【问题标题】:LINQ query and Array of stringLINQ 查询和字符串数组
【发布时间】:2012-02-15 10:46:16
【问题描述】:

我有一组string 说:

String[] Fields=new String[]{RowField,RowField1}

我可以使用以下查询通过指定值来获取值是查询,即RowFieldRowField1

var Result = (
    from x in _dataTable.AsEnumerable()
    select new
    {
        Name = x.Field<object>(RowField), 
        Name1 = x.Field<object>(RowField1)
    })
    .Distinct();

但如果假设我在Array 中有很多值,例如:

String[] Fields= new String[]
{
    RowField,
    RowField1,
    RowField2,
    .......
    RowField1000
};
  • 如何在不指定查询中的每个行字段的情况下使用此处的查询?
  • 如何遍历 LINQ 中的数组项?

【问题讨论】:

  • 你真的需要那么多字符串吗? DataRow 无法使用匿名类型做什么?
  • 我不认为你可以,因为你仍然需要为你的匿名类型指定属性名称,除非我误解了你在这里想要做什么?
  • 我同意这可能是一个可以通过检查您要解决的问题来解决的问题。
  • 在不使用LINQ的情况下,有没有其他方法可以达到要求?

标签: c# .net linq datatable


【解决方案1】:
var Result = (
    from x in _dataTable.AsEnumerable()
    select (
        from y in Fields
        select new KeyValuePair<string, object>(y, x))
        .ToDictionary())
    .Distinct(DictionariesComparer);

您还需要编写自己的.ToDictionary() 扩展方法和DictionariesComparer 方法(因为Dictionary 没有实现IEquatable)。

【讨论】:

  • 尝试使用以下代码: public static Dictionary ToDictionary(this Enum @enum) { var type = @enum.GetType(); return Enum.GetValues(type).Cast().ToDictionary(e => (object)e, e => Enum.GetName(type, e));但它给了我编译器错误。另外,您能否提供 .ToDictionary() 和 DictionariesComparer 方法的正确代码?
  • @sharmila 我的代码输入错误 - 我忘记了 KeyValuePair 构造函数需要的第二个参数。为什么要尝试将 Enum 转换为 Dictionary?这个枚举是什么?
  • public static Dictionary&lt;TKey, TValue&gt; ToDictionary&lt;TKey, TValue&gt;(this Dictionary&lt;TKey, TValue&gt; source) { return source.ToDictionary(kvp =&gt; kvp.key, kvp =&gt; kvp.value); }
  • 非常感谢您的帮助:) 但是上面的代码给出了错误“扩展方法必须在非泛型静态类中定义”我不希望类是静态的
  • 扩展方法只能定义为静态类中的静态方法。这是 C# 语言规范的一部分
【解决方案2】:

本质上,您希望从 DataTable 中检索特定字段,而不对字段名称进行硬编码。

以下代码将每行返回一个字典对象,其中包含您在数组中指定的字段。无需创建额外的扩展方法或比较器:

var result = (from row in _dataTable.AsEnumerable()
                 let projection = from fieldName in fields
                      select new {Name = fieldName, Value = row[fieldName]}
                 select projection.ToDictionary(p=>p.Name,p=>p.Value));            

内部选择从每个表格行中选择您需要的字段值并将它们存储在投影变量中。外部选择将此变量转换为字典

您可以遍历结果以获取特定字段,如下所示:

foreach (var row in result)
{
    Console.WriteLine(row["field1"]);
}

编辑: 上面的代码不返回不同的值。使用 group by 可以在不编写特殊比较器的情况下返回不同的值,但代码不是很漂亮:

var result = (from row in table.AsEnumerable()
                let projection = from fieldName in fields
                                select new { Name = fieldName, Value = row[fieldName] }
                group projection by projection.Aggregate((v, p) =>
                    new {
                        Name = v.Name + p.Name, 
                        Value = (object)String.Format("{0}{1}", v.Value, p.Value)
                    }) into g
                select g.FirstOrDefault().ToDictionary(p=>p.Name,p=>p.Value));  

聚合创建一个新的投影,其名称和值属性是所有名称和值字段的串联。聚合的结果用于对所有行进行分组并返回每个组的第一行。它有效,但它绝对是丑陋的。

最好创建一个简单的 DictionaryComparer,如下代码:

    public class DictionaryComparer<TKey,TValue>: EqualityComparer<Dictionary<TKey,TValue>>
    {
        public override bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y)
        {
            //True if both sequences of KeyValuePair items are equal
            var sequenceEqual = x.SequenceEqual(y);
            return sequenceEqual;
        }

        public override int GetHashCode(Dictionary<TKey, TValue> obj)
        {
            //Quickly detect differences in size, defer to Equals for dictionaries 
            //with matching sizes
            return obj.Count;
        }
    }

这允许你写:

        var result = (from row in table.AsEnumerable()
                     let projection = from fieldName in fields
                                      select new {Name = fieldName, Value = row[fieldName]}                                                                                    
                     select projection.ToDictionary(p=>p.Name,p=>p.Value))
                     .Distinct(new DictionaryComparer<string, object>());

【讨论】:

  • 谢谢..但是我怎样才能从中获取不同的值呢?
  • 什么意思?该示例展示了如何遍历行并检索特定字段值。您的意思是如何检索特定行而不是迭代?
  • 该方法不返回不同的值 :(
【解决方案3】:

没有 foreach linq 表达式。我通常创建自己的扩展方法

类似的东西:

public static void Foreach<T>(this IEnumerable<T> items, Action<T> action)
{
 foreach(T t in items)
 {
  action(t);
 }
}

但是,如果您打算将它与 Linq2SQL 一起使用,请注意,因为它可能会产生大量 db hits!

【讨论】:

  • 你能解释一下吗?我如何使用它来获得所需的输出,如我的示例所示?
  • 很抱歉,我相信我误解了您的问题。我的代码将允许您执行 Fields.Foreach(f => Console.WriteLn(f)) 之类的操作来回答您关于在 linq 内部进行迭代的问题。但是我不相信它会允许您将数组转换为对象。也许 .NET 4 动态关键字会有所帮助?我不确定我对它不是很熟悉吗?
  • action(t) 是做什么的;在这里做吗? LINQ表达式可以写在那里吗?以及如何调用该函数?
  • 动作变量是一个传入的方法。在我的评论示例中,我传入的 Func 是 Console.WriteLn (请注意,在这种情况下 Foreach T 是字符串)。您同样可以传入截断、反向或任何其他采用单个字符串参数的方法。线动作(t);调用在 T t 上传入的任何方法。但是,根据我上面的评论,我认为这不能解决您的财产问题。只是迭代 linq 问题中的元素。
猜你喜欢
  • 2021-12-11
  • 2020-04-06
  • 2010-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多