Friday, March 14, 2008

Converting Arrays to and from Delimited Strings

I figured .NET would have some strong built in support for creating delimited strings from a list or array. Turns out there's very little. For example, if you want to take an int[] and convert it to a string like 23134232233, you gotta write code.

Maybe the reason it's not there is because delimited strings can be error-prone, and serialization mechanisms support arrays. But let's forget about why, because sometimes you need to do this.

I write a few simple, yet flexible methods for accomplishing this. I used arrays, although another version of the methods could be created for IEnumerables or ICollections or whatever. For my situation, I needed only arrays so I looked past my abstraction apprehensions and just did it.

One interesting thing was that in certain situations I needed custom code to convert each item to and from its string. So my methods allow for that using a delegate. And to accomodate for all array types and for type safety, I used generic methods. So this is a cute way to blend those two features that have been around since C# 2.0.


 

        /// <summary>

        /// Takes a delimited string and returns an array of items that were converted to the type specified.  Individual value conversions

        /// are done by using the type converter ConvertFromInvariantString method.

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <param name="value"></param>

        /// <param name="separator"></param>

        /// <returns></returns>

        public static T[] ConvertDelimitedStringToArray<T>(string value, string separator)

        {

            TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));

            Func<T, string> itemConversionHandler = delegate(string itemText)

            {

                return (T)converter.ConvertFromInvariantString(itemText);

            };

 

            return ConvertDelimitedStringToArray(value, separator, itemConversionHandler);

        }

 

        /// <summary>

        /// Takes a delimited string and returns an array of items that were converted to the type specified.

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <param name="value"></param>

        /// <param name="separator"></param>

        /// <param name="itemConversionHandler">The delegate that converts each string item into the target type.</param>

        /// <returns></returns>

        public static T[] ConvertDelimitedStringToArray<T>(string value, string separator, Func<T,string> itemConversionHandler)

        {

            if (string.IsNullOrEmpty(value))

                return null;

 

            string[] pieces = value.Split(new string[]{separator},StringSplitOptions.None);

            T[] result = new T[pieces.Length];

            for (int i = 0; i < pieces.Length; i++)

            {

                result[i] = itemConversionHandler(pieces[i]);

            }

            return result;

        }

 

 

        /// <summary>

        /// Takes an array of items and converts it to a delimited string with the separator.  Individual value conversions

        /// are done by using the type converter ConvertToInvariantString method.

        /// </summary>

        public static string ConvertArrayToDelimitedString<T>(T[] value, string separator)

        {

            TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));

            Func<string, T> itemConversionHandler = delegate(T item)

            {

                return converter.ConvertToInvariantString(item);

            };

 

            return ConvertArrayToDelimitedString(value, separator, itemConversionHandler);

        }

 

        /// <summary>

        /// Takes an array of items and converts it to a delimited string with the separator and conversion handler provided.

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <param name="value"></param>

        /// <param name="separator"></param>

        /// <param name="itemConversionHandler">The delegate that converts each item into a piece of text.</param>

        /// <returns></returns>

        public static string ConvertArrayToDelimitedString<T>(T[] value, string separator, Func<string,T> itemConversionHandler)

        {

            if (value == null || value.Length == 0)

                return null;

 

            string[] textPieces = new string[value.Length];

            for (int i = 0; i < value.Length; i++)

            {

                textPieces[i] = itemConversionHandler(value[i]);

            }

            return string.Join(separator, textPieces);

        }



If you have .NET 3.5 (I think), the Func delegate is already there. If not, you can just define it yourself:

/// <summary>

/// A delegate to a methd that returns some TReturn value and has 1 parameter.

/// </summary>

/// <typeparam name="TReturn">The type of object the method returns.</typeparam>

/// <typeparam name="TParam1">The type of the first method parameter.</typeparam>

/// <returns></returns>

internal delegate TReturn Func<TReturn, TParam1>(TParam1 param1);


That's all you need, really. To demonstrate, check out the code below:

            //create some ints and make them pipe separated

            int[] ids = new int[] { 1, 2, 3, 4 };

            string delimitedList = ConvertArrayToDelimitedString<int>(ids, "|");

            ids = ConvertDelimitedStringToArray(delimitedList, "|");//and back again

 

            //now I take some dates and put only their day in a list

            DateTime[] dates = new DateTime[] { DateTime.Parse("2/29/1980"), DateTime.Parse("3/1/1980"), DateTime.Parse("3/5/1998") };

            //my own 'custom' (albeit useless) conversion code to convert each date to have its parts separated by -

            delimitedList = ConvertArrayToDelimitedString<DateTime>(dates, "|",

                delegate(DateTime item)

                {

                    return item.ToString("MM-DD-YY");//forgive me if my format string is not right

                }//note: I could have declared the delegate in a variable above, but kept it here to confuse people and hopefully make them think I am some kind of wizard (don't you love guys like that?)

            );

No comments: