I have a working function that separates every n character with whitespace, which works fine.
Here is the code (Swift 5):
extension String {
/// Creates a new string, separating characters specified by stride lenght.
/// - Parameters:
/// - stride: Desired stride lenght.
/// - separator: Character to be placed in between separations
func separate(every stride: Int, with separator: Character) -> String {
return String(self.enumerated().map { $0 > 0 && $0 % stride == 0 ? [separator, $1] : [$1] }.joined())
}
}
This prints an example string of 1234123412341234 like this
1234 1234 1234 1234
Now, how can i separate this string 1234123412341234 with multiple strides, for example white space to be set after 4th, then after 6th and then after 5th character, like this:
1234 123412 34123 4
Here's how I would do this:
// Prints sequences of bools using 1/0s for easy reading
func p<S: Sequence>(_ bools: S) where S.Element == Bool {
print(bools.map { $0 ? "1" : "0"}.joined())
}
// E.g. makeWindow(span: 3) returns 0001
func makeWindow(span: Int) -> UnfoldSequence<Bool, Int> {
return sequence(state: span) { state in
state -= 1
switch state {
case -1: return nil
case 0: return true
case _: return false
}
}
}
// E.g. calculateSpacePositions(spans: [4, 6, 5]) returns 000100000100001
func calculateSpacePositions<S: Sequence>(spans: S)
-> LazySequence<FlattenSequence<LazyMapSequence<S, UnfoldSequence<Bool, Int>>>>
where S.Element == Int {
return spans.lazy.flatMap(makeWindow(span:))
}
extension String {
func insertingSpaces(at spans: [Int]) -> String {
let spacePositions = calculateSpacePositions(spans: spans + [Int.max])
// p(spacePositions.prefix(self.count))
let characters = zip(inputString, spacePositions)
.flatMap { character, shouldHaveSpace -> [Character] in
return shouldHaveSpace ? [character, "_"] : [character]
}
return String(characters)
}
}
let inputString = "1234123412341234"
let result = inputString.insertingSpaces(at: [4, 6, 5])
print(result)
The main idea is that I want to zip(self, spacePositions)
, so that I obtain a sequence of the characters of self
, along with a boolean that tells me if I should append a space after the current character.
To calculate spacePositions
, I first started by making a function that when given an Int
input span
, would return span
false
s followed by a true
. E.g. makeWindow(span: 3)
returns a sequence that yields false, false, false, true
.
From there, it's just a matter of making one of these windows per element of the input, and joining them all together using flatMap
. I do this all lazily, so that we don't actually need to store all of these repeated booleans.
I hit one snag though. If you give the input [4, 6, 5]
, the output I would get used to be 4
characters, space, 6
characters, space, 5
characters, end. The rest of the string was lost, because zip
yields a sequence whose length is equal to the length of the shorter of the two inputs.
To remedy this, I append Int.max
on the spans
input. That way, the space positions are 000010000001000001 ...now followed by Int.max falses
.