tags:

views:

375

answers:

9

In C#, I have an array of ints, containing digits only. I want to convert this array to string.

Array example:

int[] arr = {0,1,2,3,0,1};

How can I convert this to a string formatted as: "012301"?

+3  A: 
 String.Join("", new List<int>(array).ConvertAll(i => i.ToString()).ToArray());
JSBangs
+7  A: 
string result = arr.Aggregate("", (s, i) => s + i.ToString());

(Disclaimer: If you have a lot of digits (hundreds, at least) and you care about performance, I suggest eschewing this method and using a StringBuilder, as in JaredPar's answer.)

mquander
+1 - This is nicer than what I put...
Reed Copsey
+1. I actually like this better than my own answer.
JSBangs
This is actually deceptively expensive. This has "telescoping" memory requirements, so for anything over a small array is not ideal. Sorry.
Marc Gravell
This is fine for small arrays but the fact that it is building a string on each iteration would be a concern with larger arrays or where this code is called a lot.
Paul Ruane
Why `+=` rather than `+`?
Pavel Minaev
That's absolutely true and if performance is a concern then you should not do this. If there are just a handful of digits, though, I'd write this for the sake of clarity and concision (although I'm sure tastes may vary.)
mquander
Pavel, that's a totally good point and += is meaningless here. Fixed my answer.
mquander
Yes, Aggregate() is right, but you should use StringBuilder. See my answer.
Danko Durbić
+2  A: 

You can do:

 int[] arr = {0,1,2,3,0,1};
 string results = string.Join("",arr.Select(i => i.ToString()).ToArray());

That gives you your results.

Reed Copsey
+2  A: 
string s = string.Concat(arr.Cast<object>().ToArray());
Paul Ruane
This doesn't work... arr.ToString will be "System.Int32[]"
Reed Copsey
Ah, fair enough.
Paul Ruane
+1. This is the simplest way if you don't need any delimiter, which is what the OP asked for. Of course there is no overload to support a delimiter...
John Buchanan
That's true if it worked, but C# isn't Python and unfortunately string.Concat isn't smart enough to do this automagically like this.
mquander
OK, I've modified it to turn the int[] into an object[] first.
Paul Ruane
(I think in this form, however, it will be seriously outperformed by a simple StringBuilder solution.)
Paul Ruane
It works now, but it's not very intuitive...
Reed Copsey
+13  A: 

To avoid the creation of an extra array you could do the following.

var builder = new StringBuilder();
Array.ForEach(arr, x => builder.Append(x));
var res = bulider.ToString();
JaredPar
Haven't verified this, but you may be able to pass `builder.Append` instead of the lambda expression since `builder.Append` matches the signature of the expected delegate in `Array.ForEach`.
Steve Guidi
Steve, good idea, but that won't work because `ForEach()` expects `void`, and `Append()` returns `StringBuilder`.
Danko Durbić
+1  A: 
string.Join("", (from i in arr select i.ToString()).ToArray())

In the .NET 4.0 the string.Join can use an IEnumerable<string> directly:

string.Join("", from i in arr select i.ToString())
Michael Damatov
+4  A: 

I like using StringBuilder with Aggregate(). The "trick" is that Append() returns the StringBuilder instance itself:

var sb = arr.Aggregate( new StringBuilder(), ( s, i ) => s.Append( i ) );
var result = sb.ToString();
Danko Durbić
+6  A: 

I realize my opinion is probably not the popular one, but I guess I have a hard time jumping on the Linq-y band wagon. It's nifty. It's condensed. I get that and I'm not opposed to using it where it's appropriate. Maybe it's just me, but I feel like people have stopped thinking about creating utility functions to accomplish what they want and instead prefer to litter their code with (sometimes) excessively long lines of Linq code for the sake of creating a dense 1-liner.

I'm not saying that any of the Linq answers that people have provided here are bad, but I guess I feel like there is the potential that these single lines of code can start to grow longer and more obscure as you need to handle various situations. What if your array is null? What if you want a delimited string instead of just purely concatenated? What if some of the integers in your array are double-digit and you want to pad each value with leading zeros so that the string for each element is the same length as the rest?

Taking one of the provided answers as an example:

        result = arr.Aggregate(string.Empty, (s, i) => s + i.ToString());

If I need to worry about the array being null, now it becomes this:

        result = (arr == null) ? null : arr.Aggregate(string.Empty, (s, i) => s + i.ToString());

If I want a comma-delimited string, now it becomes this:

        result = (arr == null) ? null : arr.Skip(1).Aggregate(arr[0].ToString(), (s, i) => s + "," + i.ToString());

This is still not too bad, but I think it's not obvious at a glance what this line of code is doing.

Of course, there's nothing stopping you from throwing this line of code into your own utility function so that you don't have that long mess mixed in with your application logic, especially if you're doing it in multiple places:

    public static string ToStringLinqy<T>(this T[] array, string delimiter)
    {
        // edit: let's replace this with a "better" version using a StringBuilder
        //return (array == null) ? null : (array.Length == 0) ? string.Empty : array.Skip(1).Aggregate(array[0].ToString(), (s, i) => s + "," + i.ToString());
        return (array == null) ? null : (array.Length == 0) ? string.Empty : array.Skip(1).Aggregate(new StringBuilder(array[0].ToString()), (s, i) => s.Append(delimiter).Append(i), s => s.ToString());
    }

But if you're going to put it into a utility function anyway, do you really need it to be condensed down into a 1-liner? In that case why not throw in a few extra lines for clarity and take advantage of a StringBuilder so that you're not doing repeated concatenation operations:

    public static string ToStringNonLinqy<T>(this T[] array, string delimiter)
    {
        if (array != null)
        {
            // edit: replaced my previous implementation to use StringBuilder
            if (array.Length > 0)
            {
                StringBuilder builder = new StringBuilder();

                builder.Append(array[0]);
                for (int i = 1; i < array.Length; i++)
                {
                    builder.Append(delimiter);
                    builder.Append(array[i]);
                }

                return builder.ToString()
            }
            else
            {
                return string.Empty;
            }
        }
        else
        {
            return null;
        }
    }

And if you're really so concerned about performance, you could even turn it into a hybrid function that decides whether to do string.Join or to use a StringBuilder depending on how many elements are in the array (this is a micro-optimization, not worth doing in my opinion and possibly more harmful than beneficial, but I'm using it as an example for this problem):

    public static string ToString<T>(this T[] array, string delimiter)
    {
        if (array != null)
        {
            // determine if the length of the array is greater than the performance threshold for using a stringbuilder
            // 10 is just an arbitrary threshold value I've chosen
            if (array.Length < 10)
            {
                // assumption is that for arrays of less than 10 elements
                // this code would be more efficient than a StringBuilder.
                // Note: this is a crazy/pointless micro-optimization.  Don't do this.
                string[] values = new string[array.Length];

                for (int i = 0; i < values.Length; i++)
                    values[i] = array[i].ToString();

                return string.Join(delimiter, values);
            }
            else
            {
                // for arrays of length 10 or longer, use a StringBuilder
                StringBuilder sb = new StringBuilder();

                sb.Append(array[0]);
                for (int i = 1; i < array.Length; i++)
                {
                    sb.Append(delimiter);
                    sb.Append(array[i]);
                }

                return sb.ToString();
            }
        }
        else
        {
            return null;
        }
    }

For this example, the performance impact is probably not worth caring about, but the point is that if you are in a situation where you actually do need to be concerned with the performance of your operations, whatever they are, then it will most likely be easier and more readable to handle that within a utility function than using a complex Linq expression.

That utility function still looks kind of clunky. Now let's ditch the hybrid stuff and do this:

    // convert an enumeration of one type into an enumeration of another type
    public static IEnumerable<TOut> Convert<TIn, TOut>(this IEnumerable<TIn> input, Func<TIn, TOut> conversion)
    {
        foreach (TIn value in input)
        {
            yield return conversion(value);
        }
    }

    // concatenate the strings in an enumeration separated by the specified delimiter
    public static string Delimit<T>(this IEnumerable<T> input, string delimiter)
    {
        IEnumerator<T> enumerator = input.GetEnumerator();

        if (enumerator.MoveNext())
        {
            StringBuilder builder = new StringBuilder();

            // start off with the first element
            builder.Append(enumerator.Current);

            // append the remaining elements separated by the delimiter
            while (enumerator.MoveNext())
            {
                builder.Append(delimiter);
                builder.Append(enumerator.Current);
            }

            return builder.ToString();
        }
        else
        {
            return string.Empty;
        }
    }

    // concatenate all elements
    public static string ToString<T>(this IEnumerable<T> input)
    {
        return ToString(input, string.Empty);
    }

    // concatenate all elements separated by a delimiter
    public static string ToString<T>(this IEnumerable<T> input, string delimiter)
    {
        return input.Delimit(delimiter);
    }

    // concatenate all elements, each one left-padded to a minimum length
    public static string ToString<T>(this IEnumerable<T> input, int minLength, char paddingChar)
    {
        return input.Convert(i => i.ToString().PadLeft(minLength, paddingChar)).Delimit(string.Empty);
    }

Now we have separate and fairly compact utility functions, each of which are arguable useful on their own.

Ultimately, my point is not that you shouldn't use Linq, but rather just to say don't forget about the benefits of creating your own utility functions, even if they are small and perhaps only contain a single line that returns the result from a line of Linq code. If nothing else, you'll be able to keep your application code even more condensed than you could achieve with a line of Linq code, and if you are using it in multiple places, then using a utility function makes it easier to adjust your output in case you need to change it later.

For this problem, I'd rather just write something like this in my application code:

        int[] arr = { 0, 1, 2, 3, 0, 1 };

        // 012301
        result = arr.ToString<int>();

        // comma-separated values
        // 0,1,2,3,0,1
        result = arr.ToString(",");

        // left-padded to 2 digits
        // 000102030001
        result = arr.ToString(2, '0');
Dr. Wily's Apprentice
I definitely agree. +1
David Silva Smith
You had me right up to the point where you created a string array and populated it just so you could call string.join. It's a waste of resources when you can take advantage of deferred execution and skip the string array completely. The code in Join isn't rocket science, it's a trivial exercise to write a generic 'Join' or 'Delimit' method (extension method if you must) that iterates over an Enumerator and builds the delimited string. I get the general point and agree, but I can't upvote because of the dumb sample code. Sorry Dude
Binary Worrier
P.S. You can use LINQ and get the power of deferred execution without cramming it all on one line. I agree that there are too many crammed LINQy examples out there. It reminds me of waaaaaaaaay back in the day when learning C, that folks would write one line WTF code to copy a string "while(*dst++ = *src++);" not because it was better/faster/More readable (definitely not the last), but because they COULD and it made them (OK then, me too) feel smart. Of course it should be written more clearly, regardless of how smart or not smart it makes you feel. Sorry for running on mate.
Binary Worrier
Thanks for the feedback. I won't claim to be an expert on the subject. However, I did test the following line of code, and it executed noticeably slower than an implementation that used an array:res = arr.Aggregate(string.Empty, (s, i) => s + i.ToString());For 1 million iterations of concatenating an int[] of length 100, this took about 60 seconds vs 40 seconds using an array. This seemed inefficient to me because I assume it does a concatenation on each iteration, whereas string.Join only does one concatenation.Please let me know if there is a more efficient way that I am missing.
Dr. Wily's Apprentice
This following code seems to execute about on par with two other implementations that I tried, both of which used Array.ForEach, one with an array and string.Join, the other with a StringBuilder:int i = 0;res = arr.Aggregate(new string[arr.Length], (s, x) => { s[i++] = x.ToString(); return s; }, s => string.Join(string.Empty, s));
Dr. Wily's Apprentice
By the way, I believe that you can only take advantage of deferred execution if you are using a function that returns a set of results (such as Intersect), but if you're using a function that returns a single result (like Aggregate), then the code will execute over all elements immediately. In this case, since we're attempting to return a single string value, I don't think deferred execution can be applied.
Dr. Wily's Apprentice
Thanks for the comments, a few points: 1) No point even talking about my solution if performance is a real concern, but in 90% of most work, performance probably isn't a concern. I start at the common case. 2) Generally I idiomatically return empty collections instead of nulls to reduce clutter, for just the reason you've illustrated in your early examples. 3) You're right about (the lack of) deferred execution. 4) I think it's just a matter of taste. Your final solution is probably clearer to people who come to C# from C++; I prefer a more physically concise and functional approach.
mquander
I totally agree, especially with the checking for null's functionality which bloats and makes LINQ very confusing. Might give you a buzz while writing the 1-liner, but in 12 months time the next developer is thinking a word that he couldn't say on radio.
Russell
A: 

The most effecient way is not to convert each int to a string, but rather create one string out of an array of chars. Then the garbage collection only has one new temp object to worry about.

int[] arr = {0,1,2,3,0,1};
string result = new string(Array.ConvertAll<int,char>(arr, x => Convert.ToChar(x + 0x30)));
Jeffrey L Whitledge