2

Im looking for a general and functional solution to concatenate x elements of an array to a new array of strings. Beginning from the first element, first with second, second with third and so on ... Sorry for the explanation not being very clear, but it has been hard for me to put on name on what I'm trying to achieve. So I will try to demonstrate it with examples.

1) If I'm trying to concatenate the letters 2 by 2 I'm using this function:

    var letters = ["a","b","c","d","e"]

   func concatenateStrings(array: [String]) -> [String] {
        return array.reduce([], { (result: [String], item:String) -> [String] in
            guard !result.isEmpty else {
                return [item] //first item
            }

            return result + [ array[result.count - 1] + " \(item)"]
        })
    }

which produces me this result ["a", "a b", "b c", "c d", "d e"]

2) 3 by 3

  func concatenateStrings(array: [String]) -> [String] {
        return array.reduce([], { (result: [String], item:String) -> [String] in
            guard !result.isEmpty else {
                return [item] //first item
            }
            guard result.count != 1 else {
                return result + [array[result.count - 1] + " \(item)"] // second item
            }
            let first = array[result.count - 2]
            let second = array[result.count - 1]
            return result + [ "\(first) " + second + " \(item)"]
        })
    }

which gives me ["a", "a b", "a b c", "b c d", "c d e"]

My question is how can I generalize this method, if for example I want to produce a new array of strings by concatenating 4 elements, 5 elements of array and so on...

Thank you, and if there is still something unclear I will happily try to be more thorough.

2 Answers 2

4

A possible solution (Swift 3+4):

func concatenateStrings(array: [String], maxLength: Int) -> [String] {

    let nestedArray = array.reduce([[String]]()) { (result, item) -> [[String]] in
        let last = result.last ?? []
        return result + [Array((last + [item]).suffix(maxLength))]
    }
    return nestedArray.map { $0.joined(separator: " ") }
}

Example:

let letters = ["a","b","c","d","e"]
print(concatenateStrings(array: letters, maxLength: 3))
// ["a", "a b", "a b c", "b c d", "c d e"]

The function first creates a nested array of strings, for example

[["a"], ["a", "b"], ["a", "b", "c"], ["b", "c", "d"], ["c", "d", "e"]]

by appending the current item to the last element, and using suffix() to limit the length. Then the inner arrays are concatenated.

In Swift 4 you can also use reduce(into:) which creates less intermediate arrays, see SE-0171 Reduce with inout:

func concatenateStrings(array: [String], maxLength: Int) -> [String] {

    let nestedArray = array.reduce(into: [[String]]()) { (result, item) in
        let last = result.last ?? []
        result.append(Array((last + [item]).suffix(maxLength)))
    }
    return nestedArray.map { $0.joined(separator: " ") }
}
Sign up to request clarification or add additional context in comments.

5 Comments

you could also make the maxLength optional func concatenateStrings(array: [String], maxLength: Int? = nil) -> [String] { let maxLength = maxLength ?? array.count
Amazing solution Martin. Just what I needed. Thank you!
@LeoDabus: Sure. One could also make the separator a parameter. I'll keep it simple for demonstration purposes.
@MartinR btw Nice addition to the reduce method being able to mutate the result in Swift 4
@LeoDabus: Yes, that is a useful addition in Swift 4.
1

Given youth input array

let elms = ["a","b","c","d","e"]

Here's an iterative function to achieve what you need

Swift 4 version

func concatenations(elms:[String], maxLength: Int) -> [String] {

    var result:[String] = []

    var word = ""

    for elm in elms {
        if word.count >= maxLength {
            word.removeFirst()
            word.append(elm)
        } else {
            word.append(elm)
        }
        result.append(word)
    }
    return result
}

Examples

concatenations(elms: elms, maxLenght: 2)
// ["a", "ab", "bc", "cd", "de"]


concatenations(elms: elms, maxLenght: 3)
// ["a", "ab", "abc", "bcd", "cde"]


concatenations(elms: elms, maxLenght: 4)
// ["a", "ab", "abc", "abcd", "bcde"]

4 Comments

you could just use collection method dropFirst instead of word.remove(at: word.startIndex)
@LeoDabus It's definitely nicer. I updated my code. Thank you for the suggestion.
you could also make maxLenght parameter optional Int func concatenations(elms:[String], maxLenght: Int? = nil) and inside your method add let maxLenght = maxLenght ?? elms.count. And btw OP wants the characters separated by a space
@LeoDabus Actually I think maxLength is explicitly needed to address the problem the OP described.

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.