2

Problem

Given a sample string abcdef, i am trying to split that into an array of two character string elements that should results in ['ab','cd','ef'];

What i tried

I tried to iterate through the string while storing the substring in the current index in an array i declared inside the method, but am getting this output ['ab','bc','cd','de','ef']

Code I used

static string[] mymethod(string str)
{
    string[] r= new string[str.Length];
    for(int i=0; i<str.Length-1; i++)
    {
        r[i]=str.Substring(i,2);
    }
    return r;
}

Any solution to correct that with the code to return the correct output is really welcome, Thanks

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
Son of Man
  • 1,213
  • 2
  • 7
  • 27
  • 3
    In your solution, `i` doesn't look to be moving with the same length of the substring.. Always incrementing by 1 instead of by 2 (what you're wanting to be the 'cutoff' point per grouping) – Mark C. Sep 22 '21 at 12:31
  • @Mark. C, okay changing the increment variable in the code, will be back with feedback – Son of Man Sep 22 '21 at 12:36
  • 1
    You do realize that half your "r" array will be empty in your requested output? – Hans Kesting Sep 22 '21 at 12:36
  • @Hans Kestling, i mplemented the corrections as suggested and th code is working fine – Son of Man Sep 22 '21 at 12:38
  • https://stackoverflow.com/questions/1450774/splitting-a-string-into-chunks-of-a-certain-size – Johnathan Barclay Sep 22 '21 at 12:41
  • @Jonathan Barclay, i will try to find a work around for strings iwith an odd length, if i lose i will ask a new question – Son of Man Sep 22 '21 at 12:46
  • @KINYUATIMOTHYNJIRU, what should happen if the string has an odd amount of characters? – Dor Lugasi-Gal Sep 22 '21 at 12:47
  • @Dor Lugasi-Gal, i am supposed to append an underscore charcater at the end of the string if its length is odd – Son of Man Sep 22 '21 at 12:50
  • string input = "abcdef"; string[] output = input.ToCharArray().Select((x,i) => new {letter = x, pos = i}).GroupBy(x => x.pos / 2).Select(x => string.Join("",x.Select(y => y.letter ))).ToArray(); – jdweng Sep 22 '21 at 13:11
  • @hans Kestling, your right, I just called `NUnit.Assert.AreEqual(new string[]{"ab","c_"},mymethod("abc");` I expected the array returned to be `["ab","c_"]` But the Unit Test reports that the element at the second index is null p[lease help – Son of Man Sep 22 '21 at 13:22
  • @KINYUATIMOTHYNJIRU see the `List` based implementation by [Dor](https://stackoverflow.com/a/69284343/121309). You issue is a) you define the array size to be equal to string size, but you should end up with half that number (because of size 2); and b) you insert into the array at index `i`, that you just increased by 2 – Hans Kesting Sep 22 '21 at 13:44
  • @Hans Kestling, right away – Son of Man Sep 22 '21 at 13:45

5 Answers5

5

your problem was that you incremented your index by 1 instead of 2 every time

 var res = new List<string>();
 for (int i = 0; i < x.Length - 1; i += 2)
 {
     res.Add(x.Substring(i, 2));
 }

should work

EDIT: because you ask for a default _ suffix in case of odd characters amount, this should be the change:

  var testString = "odd";
  string workOn = testString.Length % 2 != 0
     ? testString + "_"
     : testString;
  var res = new List<string>();
  for (int i = 0; i < workOn.Length - 1; i += 2)
  {
      res.Add(workOn.Substring(i, 2));
  }

two notes to notice:

  • in .NET 6 Chunk() is available so you can use this as suggested in other answers
  • this solution might not be the best in case of a very long input so it really depends on what are your inputs and expectations
Dor Lugasi-Gal
  • 1,430
  • 13
  • 35
4

If you are using latest .NET version i.e (.NET 6.0 RC 1), then you can try Chunk() method,

var strChunks = "abcdef".Chunk(2); //[['a', 'b'], ['c', 'd'], ['e', 'f']]

var result = strChunks.Select(x => string.Join('', x)).ToArray(); //["ab", "cd", "ef"]

Note: I am unable to test this on fiddle or my local machine due to latest version of .NET

Prasad Telkikar
  • 15,207
  • 5
  • 21
  • 44
4

.net 6 has an IEnumerable.Chunk() method that you can use to do this, as follows:

public static void Main()
{
    string[] result = 
       "abcdef"
       .Chunk(2)
       .Select(chunk => new string(chunk)).ToArray();

    Console.WriteLine(string.Join(", ", result)); // Prints "ab, cd, ef"
}

Before .net 6, you can use MoreLinq.Batch() to do the same thing.


[EDIT] In response the the request below:

MoreLinq is a set of Linq utilities originally written by Jon Skeet. You can find an implementation by going to Project | Manage NuGet Packages and then browsing for MoreLinq and installing it.

After installing it, add using MoreLinq.Extensions; and then you'll be able to use the MoreLinq.Batch extension like so:

public static void Main()
{
    string[] result = "abcdef"
       .Batch(2)
       .Select(chunk => new string(chunk.ToArray())).ToArray();

    Console.WriteLine(string.Join(", ", result)); // Prints "ab, cd, ef"
}

Note that there is no string constructor that accepts an IEnumerable<char>, hence the need for the chunk.ToArray() above.

I would say, though, that including the whole of MoreLinq just for one extension method is perhaps overkill. You could just write your own extension method for Enumerable.Chunk():

public static class MyBatch
{
    public static IEnumerable<T[]> Chunk<T>(this IEnumerable<T> self, int size)
    {
        T[] bucket = null;
        int count  = 0;

        foreach (var item in self)
        {
            if (bucket == null)
                bucket = new T[size];

            bucket[count++] = item;

            if (count != size)
                continue;

            yield return bucket;

            bucket = null;
            count  = 0;
        }

        if (bucket != null && count > 0)
            yield return bucket.Take(count).ToArray();
    }
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • Can you elaborate more on then MoreLinq.Batch() because i happen not to have that version of C# – Son of Man Sep 22 '21 at 13:32
  • [morelinq](https://morelinq.github.io/) isn't part of the .Net Framework, but an additionnal, open source, library you can use in your projects – Steve B Sep 22 '21 at 14:30
1

With linq you can achieve it with the following way:

char[] word = "abcdefg".ToCharArray();
var evenCharacters = word.Where((_, idx) => idx % 2 == 0);
var oddCharacters = word.Where((_, idx) => idx % 2 == 1);
var twoCharacterLongSplits = evenCharacters
    .Zip(oddCharacters)
    .Select((pair) => new char[] { pair.First, pair.Second });

The trick is the following, we create two collections:

  • one where we have only those characters where the original index was even (% 2 == 0)
  • one where we have only those characters where the original index was odd (% 2 == 1)

Then we zip them. So, we create a tuple by taking one item from the even and one item from the odd collection. Then we create a new tuple by taking one item from the even and ...

And last we convert the tuples to arrays to have the desired output format.

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
0

You are on the right track but you need to increment by 2 not by one. You also need to check if the array has not ended before taking the second character else you risk running into an index out of bounds exception. Try this code I've written below. I've tried it and it works. Best!

public static List<string> splitstring(string str)
        {
            List<string> result = new List<string>();
            int strlen = str.Length;
            for(int i = 0; i<strlen; i+=2)
            {
                string currentstr = str[i].ToString();
                if (i + 1 <= strlen-1)
                { currentstr += str[i + 1].ToString(); }
                result.Add(currentstr);
            }
            return result;
        }
Olakitan
  • 114
  • 1
  • 5