Is there an easy way to return a string repeated X number of times?

Viewed 262704

I'm trying to insert a certain number of indentations before a string based on an items depth and I'm wondering if there is a way to return a string repeated X times. Example:

string indent = "---";
Console.WriteLine(indent.Repeat(0)); //would print nothing.
Console.WriteLine(indent.Repeat(1)); //would print "---".
Console.WriteLine(indent.Repeat(2)); //would print "------".
Console.WriteLine(indent.Repeat(3)); //would print "---------".
21 Answers

You can repeat your string (in case it's not a single char) and concat the result, like this:

String.Concat(Enumerable.Repeat("---", 5))

Strings and chars [version 1]

string.Join("", Enumerable.Repeat("text" , 2 ));    
//result: texttext

Strings and chars [version 2]:

String.Concat(Enumerable.Repeat("text", 2));
//result: texttext

Strings and chars [version 3]

new StringBuilder().Insert(0, "text", 2).ToString(); 
//result: texttext

Chars only:

'5' * 3; 
//result: 555

Chars only:

new string('5', 3);
//result: 555

Extension way:

(works FASTER - better for WEB)

public static class RepeatExtensions
{
    public static string Repeat(this string str, int times)
    {
        var a = new StringBuilder();
        
        //Append is faster than Insert
        ( () => a.Append(str) ).RepeatAction(times) ;
        
        return a.ToString();
    }

    public static void RepeatAction(this Action action, int count)
    {
        for (int i = 0; i < count; i++)
        {
            action();
        }
    }

}

usage:

 var a = "Hello".Repeat(3); 
 //result: HelloHelloHello

I like the answer given. Along the same lines though is what I've used in the past:

"".PadLeft(3*Indent,'-')

This will fulfill creating an indent but technically the question was to repeat a string. If the string indent is something like >-< then this as well as the accepted answer would not work. In this case, c0rd's solution using StringBuilder looks good, though the overhead of StringBuilder may in fact not make it the most performant. One option is to build an array of strings, fill it with indent strings, then concat that. To whit:

int Indent = 2;
        
string[] sarray = new string[6];  //assuming max of 6 levels of indent, 0 based

for (int iter = 0; iter < 6; iter++)
{
    //using c0rd's stringbuilder concept, insert ABC as the indent characters to demonstrate any string can be used
    sarray[iter] = new StringBuilder().Insert(0, "ABC", iter).ToString();
}

Console.WriteLine(sarray[Indent] +"blah");  //now pretend to output some indented line

We all love a clever solution but sometimes simple is best.

Print a line with repetition.

Console.Write(new string('=', 30) + "\n");

==============================

For general use, solutions involving the StringBuilder class are best for repeating multi-character strings. It's optimized to handle the combination of large numbers of strings in a way that simple concatenation can't and that would be difficult or impossible to do more efficiently by hand. The StringBuilder solutions shown here use O(N) iterations to complete, a flat rate proportional to the number of times it is repeated.

However, for very large number of repeats, or where high levels of efficiency must be squeezed out of it, a better approach is to do something similar to StringBuilder's basic functionality but to produce additional copies from the destination, rather than from the original string, as below.

    public static string Repeat_CharArray_LogN(this string str, int times)
    {
        int limit = (int)Math.Log(times, 2);
        char[] buffer = new char[str.Length * times];
        int width = str.Length;
        Array.Copy(str.ToCharArray(), buffer, width);

        for (int index = 0; index < limit; index++)
        {
            Array.Copy(buffer, 0, buffer, width, width);
            width *= 2;
        }
        Array.Copy(buffer, 0, buffer, width, str.Length * times - width);

        return new string(buffer);
    }

This doubles the length of the source/destination string with each iteration, which saves the overhead of resetting counters each time it would go through the original string, instead smoothly reading through and copying the now much longer string, something that modern processors can do much more efficiently.

It uses a base-2 logarithm to find how many times it needs to double the length of the string and then proceeds to do so that many times. Since the remainder to be copied is now less than the total length it is copying from, it can then simply copy a subset of what it has already generated.

I have used the Array.Copy() method over the use of StringBuilder, as a copying of the content of the StringBuilder into itself would have the overhead of producing a new string with that content with each iteration. Array.Copy() avoids this, while still operating with an extremely high rate of efficiency.

This solution takes O(1 + log N) iterations to complete, a rate that increases logarithmically with the number of repeats (doubling the number of repeats equals one additional iteration), a substantial savings over the other methods, which increase proportionally.

Adding the Extension Method I am using all over my projects:

public static string Repeat(this string text, int count)
{
    if (!String.IsNullOrEmpty(text))
    {
        return String.Concat(Enumerable.Repeat(text, count));
    }
    return "";
}

Hope someone can take use of it...

Using the new string.Create function, we can pre-allocate the right size and copy a single string in a loop using Span<char>.

I suspect this is likely to be the fastest method, as there is no extra allocation at all: the string is precisely allocated.

 public static string Repeat(this string source, int times)
 {
     return string.Create(source.Length * times, source, RepeatFromString);
 }
 
 private static void RepeatFromString(Span<char> result, string source)
 {
     ReadOnlySpan<char> sourceSpan = source.AsSpan();
     for (var i = 0; i < result.Length; i += sourceSpan.Length)
         sourceSpan.CopyTo(result.Slice(i, sourceSpan.Length));
 }

dotnetfiddle

I didn't see this solution. I find it simpler for where I currently am in software development:

public static void PrintFigure(int shapeSize)
{
    string figure = "\\/";
    for (int loopTwo = 1; loopTwo <= shapeSize - 1; loopTwo++)
    {
        Console.Write($"{figure}");
    }
}
Related