andy_dev
andy_dev

Reputation: 3

Splitting a string into evenly-sized chunks

I have a challenge to solve which is slitting a string into evenly-sized chunks.

Examples Given the string s = "abcdefg" and chunks = 3, return ["abc", "de", "fg"]. Note that the remainder of 1 was distributed to the first element in the list.

Given the string s = "abcdefgh" and chunks = 3, return ["abc", "def", "gh"]. Again, note that the remainder of 2 was distributed to the first element in the list followed by the second element in the list.

So, I have this solution from an old question but the "evenly" isn't consider

        public static IEnumerable<string> Iterator(string s, int chunks)
        {
            for (int i = 0; i < s.Length; i += s.Length - chunks)
            {
                if (i + chunks > s.Length) chunks = s.Length - i;
                yield return s.Substring(i, chunks);

            }

        }


        public static string[] Result(string s, int chunks)
        {
            if (chunks <= 0)
                throw new ArgumentException();

            return Iterator(s, chunks).ToArray();
        }

Second example would pass with this but the first doesn't, how do I adjust this to pass both test?

Upvotes: -1

Views: 1322

Answers (1)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112382

You are confusing the number of chunks with the chunk size.

You must calculate the chunk size with:

int chunkSize = s.Length / chunks;

If the length of the string is not divisible by chunks, this will truncate the result because integer arithmetic is performed here. E.g., if the string size is 7 and chunks = 3, then this will yield 2. And you have a remainder of 1. If the string size was 8, the chunk size would still be 2, but the remainder would be 2. Now, you must distribute this remainder among the chunks.

You can get the remainder with the modulo operator %:

int remainder = s.Length % chunks;

Since you want the first chunks to be bigger, we now attribute this remainder to the first chunks:

int start = 0;
while (start < s.Length)
{
    int thisChunkSize = chunkSize;
    if (remainder > 0)
    {
        thisChunkSize++;
        remainder--;
    }
    yield return s.Substring(start, thisChunkSize);
    start += thisChunkSize;
}

If you need an even better distribution, you can use floating point arithmetic and round. The MidpointRounding tells what happens when rounding a value with a .5 fraction.

public static IEnumerable<string> EvenIterator(string s, int chunks)
{
    int start = 0;
    var rounding = new[] { MidpointRounding.ToPositiveInfinity, 
                           MidpointRounding.ToNegativeInfinity };
    int r = 0;
    while (start < s.Length) {
        int chunkSize = (int)Math.Round((double)(s.Length - start) / chunks, rounding[r]);
        r = 1 - r; // Swap the rounding
        yield return s.Substring(start, chunkSize);
        start += chunkSize;
        chunks--;
    }
}

A test with "abcdefghijklmno" and chunk size 6 gives:

[ "abc", "de", "fgh", "ij", "klm", "no" ]

Upvotes: 1

Related Questions