Convert numbers in a string array (#3, #1, #2, #4) to range (#1:#4) Updated question: Compress numbers in a string array (#3, #1, #2, #4) to range (#1:#4)



This function takes a string array,

{"foo", "#123", "#124", "bar", "#125", "#126"}


makes a new array with the numbers converted to a range:

{"foo", "#123:126", "bar"}


and returns:

"foo,#123:126,bar"


Note it doesn't and shouldn't change {"#1", "#2"} to {"#1:#2"}



static void Main(string[] args)
{
    var ids = new string[] { "foo", "#12", "#14", "#17", "#13", "#18", "#bar", "#19", "#20", "#25", "#26", "#38", "#39", "baz", "#12", "#13", "#14" };
    Console.WriteLine("foo,#12:#14,#17:#20,#bar,#25,#26,#38,#39,baz,#12:#14");
    Console.WriteLine(Convert(ids));
    Console.Read();
}
static string Convert(IEnumerable&lt;string&gt; arr)
{
    var IDs = arr.ToList();
    var result = new List&lt;string&gt;();
    for (var index = 0; index &lt; IDs.Count; index++)
    {
        var i = IDs[index];
        if (i.StartsWith("#"))
        {
            int fromInt;
            if (int.TryParse(i.Substring(1), out fromInt))
            {
                var plus1 = $"#{fromInt + 1}";
                var plus2 = $"#{fromInt + 2}";
                if (IDs.Contains(plus1) &amp;&amp; IDs.Contains(plus2))
                {
                    IDs.Remove(plus1);
                    IDs.Remove(plus2);
                    var toInt = fromInt + 2;
                    while (IDs.Contains($"#{toInt + 1}"))
                    {
                        toInt += 1;
                        IDs.Remove($"#{toInt}");
                    }
                    i = $"#{fromInt}:#{toInt}";
                }
            }
        }
        result.Add(i);
    }
    return string.Join(",", result);
}
In order to improve readability I'd say to not go too deep with the indentation. For example:

            if (IDs.Contains(plus1) &amp;&amp; IDs.Contains(plus2))
            {
                IDs.Remove(plus1);
                IDs.Remove(plus2);
                var toInt = fromInt + 2;
                while (IDs.Contains($"#{toInt + 1}"))
                {
                    toInt += 1;
                    IDs.Remove($"#{toInt}");
                }
                i = $"#{fromInt}:#{toInt}";
            }


can be transformed in:

if (!(IDs.Contains(plus1) &amp;&amp; IDs.Contains(plus2)))
{
    continue;
}
IDs.Remove(plus1);
IDs.Remove(plus2);
var toInt = fromInt + 2;
while (IDs.Contains($"#{toInt + 1}"))
{
    toInt += 1;
    IDs.Remove($"#{toInt}");
}
i = $"#{fromInt}:#{toInt}";


The same can be done with the conditions if (int.TryParse(i.Substring(1), out fromInt)) and if (i.StartsWith("#")) in the upper levels. 

The name of the string i is a little misleading. i is usually used as an index. I think id would be a proper name.

The resulting code could be transformed from:

var newIDs = new List&lt;string&gt;();
for (var index = 0; index &lt; IDs.Count; index++)
{
    var i = IDs[index];
    if (i.StartsWith("#"))
    {
        int fromInt;
        if (int.TryParse(i.Substring(1), out fromInt))
        {
            var plus1 = $"#{fromInt + 1}";
            var plus2 = $"#{fromInt + 2}";
            if (IDs.Contains(plus1) &amp;&amp; IDs.Contains(plus2))
            {
                IDs.Remove(plus1);
                IDs.Remove(plus2);
                var toInt = fromInt + 2;
                while (IDs.Contains($"#{toInt + 1}"))
                {
                    toInt += 1;
                    IDs.Remove($"#{toInt}");
                }
                i = $"#{fromInt}:#{toInt}";
            }
        }
    }
    newIDs.Add(i);
}
return string.Join(",", newIDs);


into the following:

var newIDs = new List&lt;string&gt;();
for (var index = 0; index &lt; IDs.Count; index++)
{
    var id = IDs[index];
    if (!id.StartsWith("#"))
    {
        newIDs.Add(i);
        continue;
    }
    int fromInt;
    if (!int.TryParse(i.Substring(1), out fromInt))
    {
        newIDs.Add(id);
        continue;
    }
    var plus1 = $"#{fromInt + 1}";
    var plus2 = $"#{fromInt + 2}";
    if (!(IDs.Contains(plus1) &amp;&amp; IDs.Contains(plus2)))
    {
        newIDs.Add(id);
        continue;
    }
    IDs.Remove(plus1);
    IDs.Remove(plus2);
    var toInt = fromInt + 2;
    while (IDs.Contains($"#{toInt + 1}"))
    {
        toInt += 1;
        IDs.Remove($"#{toInt}");
    }
    i = $"#{fromInt}:#{toInt}";
    newIDs.Add(id);
}
return string.Join(",", newIDs);