5

I want to split string by chunks of given size 2

Example :

String "1234567" and output should be ["12", "34", "56","7"]

12
  • 1
    What you have tried and what is not working? Unfortunately this is not a code writing service. Commented Jan 4, 2018 at 4:56
  • 1
    Show your tried code !! Commented Jan 4, 2018 at 5:04
  • 1
    If you want to split with length of 2 why does the last expected output has 3 characters/integers ? Check this: gist.github.com/ericdke/fa262bdece59ff786fcb Commented Jan 4, 2018 at 5:14
  • @GoodSp33d I update it should be integer/string array Commented Jan 4, 2018 at 5:18
  • What is the logic behind the last group having 3 elements instead of one? Commented Jan 4, 2018 at 5:22

9 Answers 9

10

You can group your collection elements (in this case Characters) every n elements as follow:

extension Collection {
    func unfoldSubSequences(limitedTo maxLength: Int) -> UnfoldSequence<SubSequence,Index> {
        sequence(state: startIndex) { start in
            guard start < self.endIndex else { return nil }
            let end = self.index(start, offsetBy: maxLength, limitedBy: self.endIndex) ?? self.endIndex
            defer { start = end }
            return self[start..<end]
        }
    }
    func subSequences(of n: Int) -> [SubSequence] {
        .init(unfoldSubSequences(limitedTo: n))
    }
}

let numbers = "1234567"
let subSequences = numbers.subSequences(of: 2)
print(subSequences)    // ["12", "34", "56", "7"]

edit/update:

If you would like to append the exceeding characters to the last group:

extension Collection {
    func unfoldSubSequencesWithTail(lenght: Int) -> UnfoldSequence<SubSequence,Index> {
        let n = count / lenght
        var counter = 0
        return sequence(state: startIndex) { start in
            guard start < endIndex else { return nil }
            let end = index(start, offsetBy: lenght, limitedBy: endIndex) ?? endIndex
            counter += 1
            if counter == n {
                defer { start = endIndex }
                return self[start...]
            } else {
                defer { start = end }
                return self[start..<end]
            }
        }
    }
    func subSequencesWithTail(n: Int) -> [SubSequence] {
        .init(unfoldSubSequencesWithTail(lenght: n))
    }
}

let numbers = "1234567"
let subSequencesWithTail = numbers.subSequencesWithTail(n: 2)
print(subSequencesWithTail)    // ["12", "34", "567"]
Sign up to request clarification or add additional context in comments.

5 Comments

Great answer !!
@LeoDabus Playground execution failed: error: MyPlayground.playground:6:27: error: argument type 'String' does not conform to expected type 'Sequence' let chars = Array(self)
If you are using Swift 3. Just change it to Array(characters)
@LeoDabus it's swift4
Try creating a new playground file and make sure you are using my code as it is.
2
var testString = "abcdefghijklmnopqrstu"
var startingPoint: Int = 0
var substringLength: Int = 1
var substringArray = [AnyHashable]()
for i in 0..<(testString.count ?? 0) / substringLength {
    var substring: String = (testString as NSString).substring(with: NSRange(location: startingPoint, length: substringLength))
    substringArray.append(substring)
    startingPoint += substringLength
}
print("\(substringArray)")

OutPut : ( a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u )

2 Comments

Code required in swift
Here change substringLength from 1 to 2.
2

try this

func SplitString(stringToBeSplitted:String, By:Int) -> [String]
    {
        var newArray = [String]()
        var newStr = String()
        for char in stringToBeSplitted
        {
            newStr += String(char)
            if newStr.count == By
            {
                newArray.append(newStr)
                newStr = ""
            }

        }
        return newArray
    }

Comments

2

Swift 5

extension Array {
    func chunks(size: Int) -> [[Element]] {
        return stride(from: 0, to: count, by: size).map {
            Array(self[$0 ..< Swift.min($0 + size, count)])
        }
    }
}

extension String {
    func chunks(size: Int) -> [String] {
        map { $0 }.chunks(size: size).compactMap { String($0) }
    }
}

let s = "1234567"
print(s.chunks(size: 2)) // ["12", "34", "56", "7"]

3 Comments

Tweak: the body of String.chunks could also start with Array(self)., instead of map { $0 }. Or did you write this intentionally?
Both create an array of Character, apparently no difference, but maybe Array(self) is optimized for string size, I don’t know.
Indeed, I've opted to use the Array initialiser for this reason. Also, imo it's slightly more readable and 'consistent' with the other chunks implementation.
1
extension String {
    func split(len: Int) -> [String] {
        var currentIndex = 0
        var array = [String]()
        let length = self.characters.count
        while currentIndex < length {
            let startIndex = self.startIndex.advancedBy(currentIndex)
            let endIndex = startIndex.advancedBy(len, limit: self.endIndex)
            let substr = self.substringWithRange(Range(start: startIndex, end: endIndex))
            array.append(substr)
            currentIndex += len
        }
        return array
    }
}

"123456789".split(2)

//output: ["12", "34", "56", "78", "9"]

2 Comments

This does not compile in Swift 4.
Please refer Leo Dabus accepted answer. :)
1

I have write one method in objective c as below,

-(NSMutableArray*)splitString : (NSString*)str withRange : (int)range{


NSMutableArray *arr = [[NSMutableArray alloc]init];

NSMutableString *mutableStr = [[NSMutableString alloc]initWithString:str];

int j = 0;
int counter = 0;

for (int i = 0; i < str.length; i++) {

    j++;

    if (range == j) {

        j = 0;


        if (!(i == str.length - 1)) {

             [mutableStr insertString:@"$" atIndex:i+1+counter];
        }



        counter++;
    }
}



arr = (NSMutableArray*)[mutableStr componentsSeparatedByString:@"$"];

NSLog(@"%@",arr);

return arr;
}

You can call this method like,

 [self splitString:@"123456" withRange:2];

and result will be,

(
12,
34,
56
)

2 Comments

The questions is tagged Swift.
Yes I know, I have just write for reference if it is helpful @rmaddy
1

You can also try below code:

var arrStr: [Substring] = []
    let str = "1234567"
    var i = 0
    while i < str.count - 1 {
        let index = str.index(str.startIndex, offsetBy: i)
        //Below line gets current index and advances by 2
        let substring = str[index..<str.index(index, offsetBy: 2)]
        arrStr.append(substring)
        i += 2
    }
    if str.count % 2 == 1 {
        arrStr.append(str.suffix(1))
    }
    print(arrStr)

2 Comments

please add expiation flowing line : let index = str.index(str.startIndex, offsetBy: i) let firstCharacter = str[index..<str.index(index, offsetBy: 2)]
@NazmulHasan I have edited my post. Please have a look.
1

There's a stupid way, you can think about the rules of the data model.

    var strOld = "123456"
    print("The original string:\(strOld)")

    strOld.insert("、", at: strOld.index(before: strOld.index(strOld.startIndex, offsetBy: 3)))

    strOld.insert("、", at: strOld.index(before: strOld.index(strOld.startIndex, offsetBy: 6)))

    print("After inserting:\(strOld)")

    let str = strOld

    let splitedArray = str.components(separatedBy: "、")
    print("After the split of the array:\(splitedArray)")

    let splitedArrayOther = str.split{$0 == "、"}.map(String.init)
    print("After break up the array (method 2):\(splitedArrayOther)")

The results:

The original string:123456
After inserting:12、34、56
After the split of the array:["12", "34", "56"]
After break up the array (method 2):["12", "34", "56"]

Comments

1

Here's a short (and clean) solution, thanks to recursion:

extension Collection {
    func chunks(ofSize size: Int) -> [SubSequence] {
        // replace this by `guard count >= size else { return [] }`
        // if you want to omit incomplete chunks
        guard !isEmpty else { return [] }
        return [prefix(size)] + dropFirst(size).chunks(ofSize: size)
    }
}

The recursion should not pose a performance problem, as Swift has support for tail call optimization.

Also if Swift arrays are really fast when it comes to prepending or appending elements (like the Objective-C ones are), then the array operations should be fast.

Thus you get both fast and readable code (assuming my array assumptions are true).

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.