Mexican wave in Swift string - arrays

I need to print a String as if a Mexican Wave is passing through each character in the String:
wave("hello") -> ["Hello", "hEllo", "heLlo", "helLo", "hellO"]
For current moment I'm stopped at:
var str = "hel lo"
var arr = [String]()
str = str.lowercased()
str = str.replacingOccurrences(of: " ", with: "")
for i in str {
arr.append (str.replacingOccurrences(of: "\(i)", with: i.uppercased()))
}
print(arr)

You can simply map your string indices and replace its character at each iteration:
let string = "hello"
let result = string.indices.map {
string.replacingCharacters(in: $0...$0, with: string[$0...$0].uppercased())
}
print(result)
This will print:
["Hello", "hEllo", "heLlo", "helLo", "hellO"]

After many iterations and doc reviews, also find this solution:
func wave(_ y: String) -> [String] {
if y.count == 0 {
return [String]()
}
var wave = [String]()
for i in y.indices {
if !y[i].isWhitespace{
wave.append(y[..<i] + y[i].uppercased() + y[y.index(after: i)...])
}
}
return wave
}

Related

Need to get certain words from a long string swift

So I have a this long text string and I need to get the words that are in the { } so I can put these in an array.
This is the string: <p style=\"text-align:center\">There will be some text here.<br><br>Send More Text in here<br><br>${LogoSquare} ${WUSquare} ${VisaSquare}</p>"
I have a regEx to see when I've reach the words that I need:
static let regEX = "^.*\\b(LogoSquare|WUSquare|VisaSquare)\\b.*$"
With the following function
fun containsValues(text: String) -> Bool {
if let regex = try? NSRegularExpression(pattern: AdviceCardRegexPattern.mytd, options: [.dotMatchesLineSeparators]) {
return (regex.firstMatch(in: text, options: [], range: NSRange(location: 0, length: text.count)) != nil)
}
return false
}
This is what I've tried so far:
var components = textPayload.components(separatedBy: ">")
I do the above so that the values get separated from the last > so it becomes like this
"${LogoSquare} ${WUSquare} ${VisaSquare}</p", ""]
Then I have:
let removal: [Character] = ["$", "{", "}", "<", "/"]
var imageTags: [String] = []
for value in components {
if containsValues(text: value) {
imageTags.append(value.filter { !removal.contains($0)})
}
}
This prints the following:
["LogoSquare WUSquare VisaSquarep"]
The only thing that's left is getting rid of the p at the end of VisaSquare
You're over-thinking this. Just match against ${something} and capture the something. For instance:
let s = "hey nonny nonny ${LogoSquare} ${WUSquare} ${VisaSquare} yoho yo"
let r = try! NSRegularExpression(pattern: "\\$\\{(.*?)\\}")
var result = [String]()
let ss = s as NSString
for m in r.matches(in: s, options: [], range: NSMakeRange(0, ss.length)) {
result.append(ss.substring(with: m.range(at: 1)))
}
print(result)

Convert String containing Array of Integer to Integer Array IN Swift

I have a
let locationjson: String = "[\"43786\",\"55665\",\"62789\",\"90265\"]"
And I want to convert it to Arraylist / List in Swift... I have searched on StackOverflow but couldn't find an appropriate solution for Swift.
I want to output as List<Integer> containing the values [43786,55665,62789,90265].
As Martin mentioned in the comments JSONSerialization is your friend:
let locationjson = "[\"43786\",\"55665\",\"62789\",\"90265\"]"
let data = locationjson.data(using: .utf8)!
if let array = (try? JSONSerialization.jsonObject(with: data)) as? [String] {
let intArray = array.flatMap { Int($0) }
print(intArray)
}
You can do this using flatMap:
let locationjson = ["43786", "55665", "62789", "90265"]
let result = locationjson.flatMap { Int($0) }
You mean something like this?
var str:String = "[\"1\",\"2\",\"3\",\"4\",\"5\",\"6\"]"
str = str.replacingOccurrences(of: "[", with: "")
str = str.replacingOccurrences(of: "]", with: "")
str = str.replacingOccurrences(of: "\"", with: "")
var arrStrNums = str.components(separatedBy: ",")
var nums:[Int] = []
for strNum in arrStrNums {
if let num = Int(strNum) {
nums.append(num)
}
}
print("Number list: \(nums)")
Outputs:
Number list: [1, 2, 3, 4, 5, 6]

is there a better way to align this column of numbers in a Swift textView?

I need to format a column of numbers in textView by displaying a column of fractional and decimal numbers and aligning them so the decimal point or forward slash characters appear in the same column like so ...
x 1/1
1 76.04900
x 193.15686
x 310.26471
4 5/4
x 503.42157
6 579.47057
x 696.57843
x 25/16
9 889.73529
x 1006.84314
11 1082.89214
This was achieved in Swift by inserting the appropriate number of leading whitespace characters and using left justification with a font where the column width is the same for each character.
To calculate the number of whitespaces, characters in the remainder or the dividend are excluded from the total number of characters in each number. This simple task involved subtracting .index(of: ) from .count and was complicated by the fact that .index(of:) returns an optional Array.Index? not an Int?
I suspect there might be a simpler way to achieve the same result and welcome any improvements to the Swift code below that might make life easier for whoever has to maintain it. The code was extracted from my project and has been tested in Playground.
Note. textMessage is a single string. It is currently displayed in the Debug area. It will eventually be displayed in a textView.
//: Playground
import UIKit
var tuningArray = ["1/1", "76.04900", "193.15686", "310.26471", "5/4", "503.42157", "579.47057", "696.57843", "25/16", "889.73529", "1006.84314", "1082.89214"]
var keyArray = ["x", "1", "x", "x", "4", "x", "6", "x", "x", "9", "x", "11"]
var characterCountArray = [Int]()
var scaleSize = Int()
var textMessage = String()
func formatTextMessage() {
var formattedTextArray = [String](repeating: "", count: scaleSize)
for i in 0..<scaleSize {
let element = tuningArray[i]
countNumericCharactersBeforeDotOrSlash (s: element)
}
let largestStringSize = characterCountArray.reduce(Int.min, { max($0, $1) })
for i in 0..<scaleSize {
let thisStringSize = characterCountArray[i]
let blanks = largestStringSize - thisStringSize
let filler = insertWhiteSpace(count: blanks)
formattedTextArray[i].append("\t" + keyArray[i] + "\t" + filler + tuningArray[i] + "\n")
textMessage += formattedTextArray[i]
}
print(textMessage)
}
func insertWhiteSpace(count: Int) -> String {
var filler = ""
for _ in 0..<count {
filler = filler + " "
}
return filler
}
func countNumericCharactersBeforeDotOrSlash (s: String) {
let fractionalNumber = findFractionalNumber(s: s)
let decimalNumber = findDecimalNumber(s: s)
var thisStringSize = 0
if !fractionalNumber && !decimalNumber {
print("Error: invalid number format")
}
if fractionalNumber == true {
let pitchStringArray = Array(s.characters)
let endIndex = pitchStringArray.count
if let slashIndex = pitchStringArray.index(of: "/") {
let dividendFiller = endIndex - slashIndex
thisStringSize = s.characters.count - dividendFiller
}
} else {
if decimalNumber == true {
let pitchStringArray = Array(s.characters)
let endIndex = pitchStringArray.count
if let dotIndex = pitchStringArray.index(of: ".") {
let remainderFiller = endIndex - dotIndex
thisStringSize = s.characters.count - remainderFiller
}
}
}
characterCountArray.append(thisStringSize)
}
func findDecimalNumber(s: String?) -> Bool {
if (s?.contains(".")) == true {
return true
}
return false
}
func findFractionalNumber(s: String?) -> Bool {
if (s?.contains("/")) == true {
return true
}
return false
}
scaleSize = tuningArray.count
formatTextMessage()
You could use attributed text with custom tab stops, like in this example:
import UIKit
import PlaygroundSupport
var tuningArray = ["1/1", "76.04900", "193.15686", "310.26471", "5/4", "503.42157", "579.47057", "696.57843", "25/16", "889.73529", "1006.84314", "1082.89214"]
var keyArray = ["x", "1", "x", "x", "4", "x", "6", "x", "x", "9", "x", "11"]
let scaleSize = tuningArray.count
var textMessage = ""
for i in 0..<scaleSize {
var textLine = "\t" + keyArray[i] + "\t" + tuningArray[i] + "\n"
textMessage += textLine
}
let paragraphStyle = NSMutableParagraphStyle()
let decimalTerminators:CharacterSet = [".", "/"]
let decimalTabOptions = [NSTabColumnTerminatorsAttributeName:decimalTerminators]
let decimalTabLocation = CGFloat(100) // TODO: Calculate...
var decimalTab = NSTextTab(textAlignment: .natural, location: decimalTabLocation, options: decimalTabOptions)
var leftTab = NSTextTab(textAlignment: .left, location: CGFloat(0.01), options: [:])
paragraphStyle.tabStops = [leftTab, decimalTab]
var a = NSAttributedString(string:textMessage,attributes: [NSParagraphStyleAttributeName: paragraphStyle])
let tv = UITextView(frame:CGRect(x:0, y:0, width:400, height:200))
tv.attributedText = a
PlaygroundPage.current.liveView = tv
The only drawback is, that you have to calculate the position of the decimal tab somehow; in the example I just use CGFloat(100) to get a nice value. But maybe you could simply live with a constant like this.
Greg,
Instead of using space to set columns use tab (\t) tree for times based on columns size you need.
That will help to manage code better way with very less and powerful coding.
Thanks

How can I put each word of a string into an array in Swift?

Is it possible to put each word of a string into an array in Swift?
for instance:
var str = "Hello, Playground!"
to:
var arr = ["Hello","Playground"]
Thank you :)
edit/update:
Xcode 10.2 • Swift 5 or later
We can extend StringProtocol using collection split method
func split(maxSplits: Int = Int.max, omittingEmptySubsequences: Bool = true, whereSeparator isSeparator: (Character) throws -> Bool) rethrows -> [Self.SubSequence]
setting omittingEmptySubsequences to true and passing a closure as predicate. We can also take advantage of the new Character property isLetter to split the string.
extension StringProtocol {
var words: [SubSequence] {
return split { !$0.isLetter }
}
}
let sentence = "• Hello, Playground!"
let words = sentence.words // ["Hello", "Playground"]
Neither answer currently works with Swift 4, but the following can cover OP's issue & hopefully yours.
extension String {
var wordList: [String] {
return components(separatedBy: CharacterSet.alphanumerics.inverted).filter { !$0.isEmpty }
}
}
let string = "Hello, Playground!"
let stringArray = string.wordList
print(stringArray) // ["Hello", "Playground"]
And with a longer phrase with numbers and a double space:
let biggerString = "Hello, Playground! This is a very long sentence with 123 and other stuff in it"
let biggerStringArray = biggerString.wordList
print(biggerStringArray)
// ["Hello", "Playground", "This", "is", "a", "very", "long", "sentence", "with", "123", "and", "other", "stuff", "in", "it"]
That last solution (from Leo) will create empty elements in the array when there are multiple consecutive spaces in the string. It will also combine words separated by "-" and process end of line characters as words.
This one won't :
extension String
{
var wordList: [String]
{
return componentsSeparatedByCharactersInSet(NSCharacterSet.alphanumericCharacterSet().invertedSet).filter({$0 != ""})
}
}

Accessing element of an array with iterator instead of position in Swift

I would like to know if there are iterators for array in Swift, like in CPP, which permits to avoid the using of position to access to the next element of an array.
Actually, I use position + 1 in my array to access its next element:
var array = ["a", "b", "c", "d"]
func arrayToString(success : String -> Void)
{
var str = String()
var myFunc : (Int -> Void)!
myFunc = {
if $0 < array.count
{
str += array[$0]
myFunc($0 + 1)
}
else
{
success(str)
}
}
myFunc(0)
}
arrayToString({
println($0)
})
I'm looking for a solution usable like this:
var array = ["a", "b", "c", "d"]
func arrayToString(success : String -> Void)
{
var str = String()
var myFunc : (Iterator -> Void)!
myFunc = {
if $0 != nil
{
str += array[$0]
myFunc($0.next)
}
else
{
success(str)
}
}
myFunc(array.first)
}
arrayToString({
println($0)
})
Any suggestions ?
What you want is to use a generator:
var array = ["a", "b", "c", "d"]
var g = array.generate()
var str = String()
for (var i = g.next(); i != nil; i = g.next()) {
str += i!
}
This is very handy when you want to parse command line arguments:
var args = Process.arguments.generate()
while let arg = args.next() {
if (arg == "--output") {
let output = args.next()!
...
}
}
In Swift 3, this now becomes:
var args = Process.arguments.makeIterator()
while let arg = args.next() {
if (arg == "--output") {
let output = args.next()!
...
}
}
- I can think of a work around which can be implemented here, and its the use of map function of Array.
Iteration over each Elements:
let arr = [1, 2, 3, 4, 5]
arr.map { a in print("\(a + 1) ") }
Output:
2 3 4 5 6
- So in this way you can achieve the iteration over each element of the Array in Swift.

Resources