sharmila
sharmila

Reputation: 1533

LINQ query and Array of string

I have a array of string say:

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

In which I can use the below query to get the values by specifying the values is query i.e RowField and RowField1:

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

But if suppose I have many values in the Array like:

String[] Fields= new String[]
{
    RowField,
    RowField1,
    RowField2,
    .......
    RowField1000
};

Upvotes: 0

Views: 4195

Answers (3)

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131188

Essentially, you want to retrieve specific fields from a DataTable without hardcoding the field names.

The following code will return a single dictionary object per row with the fields you specify in your array. There is no need to create additional extension methods or comparers:

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

The inner select picks the field values you need from each table row and stores them in the projection variable. The outer select converts this variable in a Dictionary

You can iterate over the result to get specific fields like this:

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

EDIT: The above code doesn't return distinct values. It is possible to return distinct values without writing a special comparer using group by but the code is not very pretty:

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

The Aggregate creates a new projection whose Name and Value properties are the concatenation of all name and value fields. The result of the aggregate is used to group all rows and return the first row of each group. It works but it is definitely ugly.

It would be better to create a simple DictionaryComparer like the following code:

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

This allows you to write:

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

Upvotes: 2

penartur
penartur

Reputation: 9912

var Result = (
    from x in _dataTable.AsEnumerable()
    select (
        from y in Fields
        select new KeyValuePair<string, object>(y, x))
        .ToDictionary())
    .Distinct(DictionariesComparer);

You'll also need to write your own .ToDictionary() extension method and DictionariesComparer method (as Dictionary doesn't implement IEquatable).

Upvotes: 3

Liath
Liath

Reputation: 10191

There is no foreach linq expression. I typically create my own extension method

Something along the lines of:

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

However beware if you're planning on using this with Linq2SQL as it could create a lot of db hits!

Upvotes: 0

Related Questions