How Add Separator to String at Every N Characters in Swift

How add separator to string at every N characters in swift?

Swift 5.2 • Xcode 11.4 or later

extension Collection {

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

func every(n: Int) -> UnfoldSequence {
sequence(state: startIndex) { index in
guard index < endIndex else { return nil }
defer { let _ = formIndex(&index, offsetBy: n, limitedBy: endIndex) }
return self[index]
}
}

var pairs: [SubSequence] { .init(unfoldSubSequences(limitedTo: 2)) }
}


extension StringProtocol where Self: RangeReplaceableCollection {

mutating func insert(separator: S, every n: Int) {
for index in indices.every(n: n).dropFirst().reversed() {
insert(contentsOf: separator, at: index)
}
}

func inserting(separator: S, every n: Int) -> Self {
.init(unfoldSubSequences(limitedTo: n).joined(separator: separator))
}
}

Testing

let str = "112312451"

let final0 = str.unfoldSubSequences(limitedTo: 2).joined(separator: ":")
print(final0) // "11:23:12:45:1"

let final1 = str.pairs.joined(separator: ":")
print(final1) // "11:23:12:45:1"

let final2 = str.inserting(separator: ":", every: 2)
print(final2) // "11:23:12:45:1\n"

var str2 = "112312451"
str2.insert(separator: ":", every: 2)
print(str2) // "11:23:12:45:1\n"

var str3 = "112312451"
str3.insert(separator: ":", every: 3)
print(str3) // "112:312:451\n"

var str4 = "112312451"
str4.insert(separator: ":", every: 4)
print(str4) // "1123:1245:1\n"

How do you split a string at every n-th character in Swift?

Implement this function

extension String {
func inserting(separator: String, every n: Int) -> String {
var result: String = ""
let characters = Array(self.characters)
stride(from: 0, to: characters.count, by: n).forEach {
result += String(characters[$0.. if $0+n < characters.count {
result += separator
}
}
return result
}
}

call it this way,

let str = "XQQ230IJFEKJLDSAIOUOIDSAUIFOPDSFE28"
let final = str.inserting(separator: " ", every: 8)
print(final)

Output will be like this,

XQQ230IJ FEKJLDSA IOUOIDSA UIFOPDSF E28

This will be generic solution if you want to add any character instead of space, it will work.

Swift format string with separator every n characters where n changes

The recursive approach is likely the most maintainable and flexible for this. It can be made fully configurable and can become an extension of the String type.

For example:

extension String
{
func group(by groupSize:Int=3, separator:String="-") -> String
{
if characters.count <= groupSize { return self }

let splitSize = min(max(2,characters.count-2) , groupSize)
let splitIndex = index(startIndex, offsetBy:splitSize)

return substring(to:splitIndex)
+ separator
+ substring(from:splitIndex).group(by:groupSize, separator:separator)
}
}

string.group(by:3)

How to add substring as separator to paragraph after every N words

You convert the string to an array, and then use map to add the separator:

extension String {
func add(separator: String, afterNWords: Int) -> String {
return split(separator: " ").enumerated().map { (index, element) in
index % afterNWords == afterNWords-1 ? "\(element) \(separator)" : String(element)
}.joined(separator: " ")
}
}

//Usage:

let result = paragraph.add(separator: "$$$$", afterNWords: 3)

Insert character after first N characters in Swift

You just need to anchor your regex pattern to the start of the string:

let regex = #"^(.{\#(every)})"#

How add separator to string at every N characters in swift?

Swift 5.2 • Xcode 11.4 or later

extension Collection {

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

func every(n: Int) -> UnfoldSequence {
sequence(state: startIndex) { index in
guard index < endIndex else { return nil }
defer { let _ = formIndex(&index, offsetBy: n, limitedBy: endIndex) }
return self[index]
}
}

var pairs: [SubSequence] { .init(unfoldSubSequences(limitedTo: 2)) }
}


extension StringProtocol where Self: RangeReplaceableCollection {

mutating func insert(separator: S, every n: Int) {
for index in indices.every(n: n).dropFirst().reversed() {
insert(contentsOf: separator, at: index)
}
}

func inserting(separator: S, every n: Int) -> Self {
.init(unfoldSubSequences(limitedTo: n).joined(separator: separator))
}
}

Testing

let str = "112312451"

let final0 = str.unfoldSubSequences(limitedTo: 2).joined(separator: ":")
print(final0) // "11:23:12:45:1"

let final1 = str.pairs.joined(separator: ":")
print(final1) // "11:23:12:45:1"

let final2 = str.inserting(separator: ":", every: 2)
print(final2) // "11:23:12:45:1\n"

var str2 = "112312451"
str2.insert(separator: ":", every: 2)
print(str2) // "11:23:12:45:1\n"

var str3 = "112312451"
str3.insert(separator: ":", every: 3)
print(str3) // "112:312:451\n"

var str4 = "112312451"
str4.insert(separator: ":", every: 4)
print(str4) // "1123:1245:1\n"

How to add substring as separator to paragraph after every N words

You convert the string to an array, and then use map to add the separator:

extension String {
func add(separator: String, afterNWords: Int) -> String {
return split(separator: " ").enumerated().map { (index, element) in
index % afterNWords == afterNWords-1 ? "\(element) \(separator)" : String(element)
}.joined(separator: " ")
}
}

//Usage:

let result = paragraph.add(separator: "$$$$", afterNWords: 3)

How to separate characters in String by whitespace with multiple strides?

Here's how I would do this:

// Prints sequences of bools using 1/0s for easy reading
func p(_ 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 {
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(spans: S)
-> LazySequence>>>
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 falses 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.



Related Topics



Leave a reply



Submit