2
\$\begingroup\$

Basically I have an array of strings. In this array, a range separator word might exist. For this example, I'm going to use the word to as range separator. We can assume that the range separator is not the first or last entry in the array. If we find the range separator we need to get the entry before and the entry after the separator, compare the two entries that we can assume are safe to parse as integer and find the character that differs between them. We then need to create new entries by counting up the number that is different, and return an IEnumerable<string> containing the new entries. We can also assume that only one character will differ between the start of the range and the end of the range.

For example, if the array contains these values:

1102
to
1602

we need to return

1202
1302
1402
1502

And here's my code:

public static IEnumerable<string> GetRangeSegments(string[] segments, string rangeIndicator)
{
    var rangeIndicatorIndex = Array.IndexOf(segments, rangeIndicator);
    var rangeSegments = new string[] { };

    if (rangeIndicatorIndex < 0)
    {
        return rangeSegments;
    }

    var rangeStart = segments[rangeIndicatorIndex - 1];
    var rangeEnd = segments[rangeIndicatorIndex + 1];

    var startChars = rangeStart.ToCharArray();
    var endChars = rangeEnd.ToCharArray();

    if (startChars.Length != endChars.Length)
    {
        return rangeSegments;
    }

    var diffIndex = -1;

    for (var i = 0; i < startChars.Length; i++)
    {
        if (startChars[i] == endChars[i])
        {
            continue;
        }

        diffIndex = i;
        break;
    }

    if (diffIndex == -1)
    {
        return rangeSegments;
    }

    var start = int.Parse(startChars[diffIndex].ToString()) + 1;
    var end = int.Parse(endChars[diffIndex].ToString());

    var numbers = Enumerable.Range(start, end - start);
    return rangeSegments.Concat(numbers.Select(number => rangeStart.Remove(diffIndex, 1).Insert(diffIndex, number.ToString())));
}
\$\endgroup\$
1
  • \$\begingroup\$ @Heslacher That shouldn't happen in my case, but if it does it should probably return an empty collection. \$\endgroup\$
    – spersson
    Commented Dec 15, 2017 at 13:36

2 Answers 2

1
\$\begingroup\$

If a method returns an IEnumerable<T> we expect an deffered execution using the yield keyword.

To make this happen and have a valid parameter validation we need another method which calls the method which is yielding.

So let us add one

public static IEnumerable<string> GetRangeSegments(string[] segments, string rangeIndicator)
{
    if (segments==null) { throw new ArgumentNullException(nameof(segments)); }
    if (rangeIndicator == null) { throw new ArgumentNullException(nameof(rangeIndicator)); }
    if (string.IsNullOrWhiteSpace(rangeIndicator)) { throw new ArgumentException("May not be empty.", nameof(rangeIndicator)); }

    return GetSegments(segments, rangeIndicator);
}

All the if statements will be executed and if the code comes to return it will immediately return because it knows about the yieldings in the GetSegments() method.

Using yield makes the empty string[] rangeSegments superflous because we just yield break. If you call ToArray() on the result of these yield break you get an empty array.

Because you can access the chars of a string by using its index you don't need ToCharArray().

If there isn't by some mistake either rangeStart[diffIndex] or rangeEnd[diffIndex] a digit the code blows up at the call to int.Parse(). A better way would be to use int.TryParse() and if this doesn't succeed you could yield break.

But if we e.g do int start = rangeStart[diffIndex] + 1; then the ascii value of that char will be casted to an int which we then can check if 47 < start < 58 and if not we yield break here.

The return statmenet in your method is just unreadable, hence hard to maintain as well. To "replace" a single char inside a string we can just use a StringBuilder.

Summing all up lead to

public static IEnumerable<string> GetRangeSegments(string[] segments, string rangeIndicator)
{
    if (segments == null) { throw new ArgumentNullException(nameof(segments)); }
    if (rangeIndicator == null) { throw new ArgumentNullException(nameof(rangeIndicator)); }
    if (string.IsNullOrWhiteSpace(rangeIndicator)) { throw new ArgumentException("May not be empty.", nameof(rangeIndicator)); }
    return GetSegments(segments, rangeIndicator);
}

private const int ascii0 = 48;
private const int ascii9 = 57;
private static IEnumerable<string> GetSegments(string[] segments, string rangeIndicator)
{
    var rangeIndicatorIndex = Array.IndexOf(segments, rangeIndicator);

    if (rangeIndicatorIndex < 0 || rangeIndicatorIndex == segments.Length - 1)
    {
        yield break;
    }

    var rangeStart = segments[rangeIndicatorIndex - 1];
    var rangeEnd = segments[rangeIndicatorIndex + 1];

    if (rangeStart.Length != rangeEnd.Length)
    {
        yield break;
    }

    var diffIndex = GetFirstDifferentCharPosition(rangeStart, rangeEnd);
    if (diffIndex == -1)
    {
        yield break;
    }

    var start = rangeStart[diffIndex] + 1;
    if (start < ascii0 || start > ascii9) { yield break; }

    var end = rangeEnd[diffIndex];
    if (end < ascii0 || end > ascii9) { yield break; }

    if (end < start) { yield break; }

    StringBuilder sb = new StringBuilder(rangeStart);
    for (int i = start; i < end; i++)
    {
        sb[diffIndex] = (char)i;
        yield return sb.ToString();
    }

}
private static int GetFirstDifferentCharPosition(string first, string second)
{
    var diffIndex = -1;

    for (var i = 0; i < first.Length; i++)
    {
        if (first[i] != second[i])
        {
            diffIndex = i;
            break;
        }
    }
    return diffIndex;
}
\$\endgroup\$
4
  • \$\begingroup\$ I'm not going to upvote your answer this time because I bet it's about winning another hat, probably for not getting any votes ;-P \$\endgroup\$
    – t3chb0t
    Commented Dec 15, 2017 at 16:22
  • \$\begingroup\$ Thanks, great answer. One simple question, I realize it doesn't have any impact on functionality, but sometimes you write the yield break on the same line as the condition, and sometimes you don't. Are there any general guidelines for this? Or is it just when it looks nice to do it? \$\endgroup\$
    – spersson
    Commented Dec 18, 2017 at 9:35
  • \$\begingroup\$ Just mixing styles, something one shouldn't do :-( \$\endgroup\$
    – Heslacher
    Commented Dec 18, 2017 at 9:41
  • \$\begingroup\$ You should choose one style to use and stick to it. Here in the edit windows the yield break; would have to far on the right side so I placed it on a new line which I should have done for the others as well. \$\endgroup\$
    – Heslacher
    Commented Dec 18, 2017 at 9:44
-1
\$\begingroup\$

Here is my implementation of this. I chose a simpler way to go about it:

    static void Main(string[] args)
    {

        IEnumerable<string> retorno = GetInterval(new string[] { "1201", "to", "1601" }, "to");
        foreach(var item in retorno)
        {
            Console.WriteLine(item);
        }
        Console.Read();
    }

    public static IEnumerable<string> GetInterval(string[] segments, string separator)
    {
        List<string> lReturn = new List<string>();
        int separatorIndex = Array.IndexOf(segments, separator);
        string first = segments[separatorIndex - 1];
        string second = segments[separatorIndex + 1];
        int indexDiffer = -1;
        bool isDifferent = false;

        while (!(isDifferent = first[indexDiffer] != second[indexDiffer])) { indexDiffer++; };

        if (!isDifferent)
            return null;

        var counter = Convert.ToInt32(first.Remove(indexDiffer + 1)) + 1;
        var endRange = Convert.ToInt32(second.Remove(indexDiffer + 1));

        for (; counter < endRange; counter++)
            lReturn.Add(string.Concat(counter, first.Substring(indexDiffer + 1)));

        return lReturn;
    }

In my implementation i try to not use Linq, it will probably give you a better performance because you are controlling the iterations, also it gives you better maintenance.

\$\endgroup\$
2
  • 3
    \$\begingroup\$ Please describe your changes. Pure code dumps are off-topic and may be deleted. \$\endgroup\$
    – t3chb0t
    Commented Dec 15, 2017 at 16:18
  • \$\begingroup\$ Hope it is better now, thank you @t3chb0t \$\endgroup\$
    – Zorkind
    Commented Dec 15, 2017 at 16:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.