Sunday, March 4, 2012

LINQ: Joining a list or collection with format. An extension of string.Join() with format


string.Join() works nice for creating a string from a list/collection with separator. We can use any string as separator and also as parameter we have to pass array of values. For example we have a collection of columns names of a table and for creating SQL select statement I need to create a statement with comma separation of those columns names. Code here
var collection = new string[] {"Id", "CountryName", "ISOName", "PrintableName"};
var joinOfCollection = string.Join(",", collection);
This will return you a string value with comma separation of those collection. 
Id,CountryName,ISOName,PrintableName
But the where clause statement will be
WHERE
     Id =@Id
AND
    ISO=@ISO

here separator is AND but in string.Join I can not give any format like “{0}=@{0}”. Also with LINQ aggregate it is difficult for giving any separator. For that I have to use substring() operation after that or have to give checking inside every iteration. If I could use string.Format() then I could append format while joining string collection. The code for solving this problem will be
public static string JoinFormat<T>(this T[] values, string separator, string format)
        {
            if (values == null)
                throw new ArgumentNullException("values");
            if (values.Length == 0 || values[0] == null)
                return string.Empty;
            if (separator == null)
                separator = string.Empty;
            var stringBuilder = new StringBuilder();
            string str1 = values[0].ToString();
            if (str1 != null)
                stringBuilder.AppendFormat(format, str1);
            for (int index = 1; index < values.Length; ++index)
            {
                stringBuilder.Append(separator);
                if (values[index] != null)
                {
                    string str2 = values[index].ToString();
                    if (str2 != null)
                        stringBuilder.AppendFormat(format, str2);
                }
            }
            return stringBuilder.ToString();
        }


This will work fine for array of data and formatting will work like this :
 var columnsList = new int[] { 1, 2, 3 };
 var joinFormat = columnsList.JoinFormat(",", "{0}=@{0}");
           

But for custom class collection like I have collection of country class and I wanted to pass the data as XML attribute.
public class Country
        {
            public string Name { get; set; }
            public string ISO { get; set; }
        }
And we have collection of data like 
var countryList = new List<Country>
                                  {
                                      new Country {Name = "United State", ISO = "US"},
                                      new Country {Name = "United Kingdom", ISO = "GB"},
                                      new Country {Name = "Bangladesh", ISO = "BD"}
                                  };
Here above extension function will not work as this does not support collection of custom class. For that I have used Action delegate so that user can define own format. Then this will support  both primitive types collections and custom type collections.
 public static string JoinFormat<T>(this IEnumerable<T> values, string separator, Func<T, string> action) 
        {
            if (values == null)
                throw new ArgumentNullException("values");
            var enumerator = values.GetEnumerator();
            var stringBuilder = new StringBuilder();

            if (enumerator.MoveNext() == false || enumerator.Current == null)
            {
                return string.Empty;

            }
            stringBuilder.Append(action(enumerator.Current));
            while (enumerator.MoveNext())
            {
                stringBuilder.Append(separator);
                if (enumerator.Current != null)
                {
                    stringBuilder.Append(action(enumerator.Current));
                }
            }

            return stringBuilder.ToString(); 
        }
This is solution for where class also making XML type elements. Lets call this method for both cases :
Where class :
 
  var columnList = new List<int> { 1, 2, 3 };
  var joinFormat = columnList.JoinFormat(",", c => string.Format("{0}=@{0}", c));
Also for XML format for Country class collection will be
 var countryList = new List<Country>
                                  {
                                      new Country {Name = "United State", ISO = "US"},
                                      new Country {Name = "United Kingdom", ISO = "GB"},
                                      new Country {Name = "Bangladesh", ISO = "BD"}
                                  };

 var joinFormat = countryList.JoinFormat("", country =>
                                                       string.Format(@"<Country Name=""{0}"" ISO=""{1}""></Country>",
                                                                       country.Name, country.ISO)
                                                        );
I am using this solution for generating SQL clauses like Select clause, Order by clause, Where clause, Update clause. I will upload solution with this code and unit tests.




Source Code link http://dl.dropbox.com/u/20275838/JoinWithFormat.zip




No comments:

Post a Comment