How can split from string to array by chunks of given size
Asked Answered
S

9

5

I want to split string by chunks of given size 2

Example :

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

Sensual answered 4/1, 2018 at 4:55 Comment(12)
What you have tried and what is not working? Unfortunately this is not a code writing service.Chloromycetin
Show your tried code !!Austine
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/fa262bdece59ff786fcbHistolysis
@Histolysis I update it should be integer/string arraySensual
What is the logic behind the last group having 3 elements instead of one?Felodese
@LeoDabus it not 3 or 2 . it's based on requirementSensual
Btw you shouldn't edit your question every 5 secondsFelodese
@LeoDabus yea sorry for that .. because I getting down votedSensual
If you don't want to get downvotes next time show an attempt to solve your issueFelodese
@LeoDabus that's sound good ...Sensual
Let us continue this discussion in chat.Sensual
Hello Brian! would you give me some explanation why I getting down voted ....Sensual
F
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"]
Felodese answered 4/1, 2018 at 5:18 Comment(5)
Great answer !!Austine
@LeoDabus Playground execution failed: error: MyPlayground.playground:6:27: error: argument type 'String' does not conform to expected type 'Sequence' let chars = Array(self)Sensual
If you are using Swift 3. Just change it to Array(characters)Felodese
@LeoDabus it's swift4Sensual
Try creating a new playground file and make sure you are using my code as it is.Felodese
G
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 )

Gaylordgaylussac answered 4/1, 2018 at 5:10 Comment(2)
Code required in swiftAustine
Here change substringLength from 1 to 2.Gaylordgaylussac
B
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
    }
Beore answered 4/1, 2018 at 5:27 Comment(0)
E
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"]
Emblematize answered 6/2, 2020 at 18:4 Comment(3)
Tweak: the body of String.chunks could also start with Array(self)., instead of map { $0 }. Or did you write this intentionally?Tetanize
Both create an array of Character, apparently no difference, but maybe Array(self) is optimized for string size, I don’t know.Emblematize
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.Tetanize
D
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"]

Deepsix answered 4/1, 2018 at 5:22 Comment(2)
This does not compile in Swift 4.Beare
Please refer Leo Dabus accepted answer. :)Deepsix
C
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
)
Colville answered 4/1, 2018 at 5:33 Comment(2)
The questions is tagged Swift.Mendacious
Yes I know, I have just write for reference if it is helpful @MendaciousColville
T
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)
Trefler answered 4/1, 2018 at 6:0 Comment(2)
please add expiation flowing line : let index = str.index(str.startIndex, offsetBy: i) let firstCharacter = str[index..<str.index(index, offsetBy: 2)]Sensual
@NazmulHasan I have edited my post. Please have a look.Trefler
Q
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"]
Quell answered 4/1, 2018 at 6:8 Comment(0)
C
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).

Congruent answered 2/9, 2019 at 21:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.