In Swift How do I iterate over an array getting 2 variables when each pair of elements is a String? and a String - swift

I want to iterate over an array String?, String repeated pair but I cannot form the "for case let (a,b) in array" correctly.
The best I have come up with is to create a temp struct of {String?, String} and create an array of the temp structs and then iterate it but I would like to skip this step.
Below is the basic example with the last for loop showing the error Xcode reports.
class Foo {
var s1: String?
var s2: String?
var s3: String?
}
let foo = Foo()
foo.s1="Test1"
foo.s2=nil
foo.s3="Test3"
let fooArray = [foo.s1, ", ", foo.s2, "; ", foo.s3,"."]
let fooArray1 = [foo.s1,foo.s2, foo.s3]
var text:String = ""
for case let prop? in fooArray1 {
text = text + prop + " / "
}
print(text)
// The above works but now I want to use a different separator
//base on the property name
text=""
for case let (prop, sep) in fooArray { // Error <= Expression Type
// [String?] is ambiguous without more context
text = text + prop + sep
}
print(text)
Here is what I have come up with
struct temp {
var prop:String?
var sep:String
init(_ prop:String?, _ sep:String) {
self.prop=prop
self.sep=sep
}
let ary:[temp] = [ temp(foo.s1,", "), temp(foo.s2,"; "), temp(foo.s3,".") ]
text = ""
for a in ary {
if let p = a.prop {
text = text + p + a.sep
}
}
print (text)
is there another way just using the for loop
for (a,b) in fooArray {
...
}

As noted by #RJE, the inferred type of fooArray, as defined in your code, is [String?].
Here is one way to make it work:
class Foo {
var s1: String?
var s2: String?
var s3: String?
}
let foo = Foo()
foo.s1 = "Test1"
foo.s2 = nil
foo.s3 = "Test3"
let fooArray1 = [foo.s1, foo.s2, foo.s3]
let separators = [", ", "; ", "."]
var text = ""
for i in fooArray1.indices {
if let p = fooArray1[i] {
text = text + p + separators[i]
}
}
print (text) //Test1, Test3.
Or
let zipped = zip(fooArray1, separators)
let text = zipped.map { tuple -> String in
if case let (x?, y) = tuple {
return x + y
} else {
return ""
}
}.joined()
print (text) //Test1,Test3.
Or
let fooArray = [foo.s1, ", ", foo.s2, "; ", foo.s3, "."]
var text = ""
var step = 1
var index = 0
while index < fooArray.count {
if let str = fooArray[index] {
step = 1
text += str
} else {
step = 2
}
index += step
}
print(text) //Test1, Test3.
It would be better to define the initializer this way :
class Foo {
var s1: String?
var s2: String?
var s3: String?
init(s1: String?, s2: String?, s3: String?) {
self.s1 = s1
self.s2 = s2
self.s3 = s3
}
}
let foo = Foo(s1: "Test1", s2: nil, s3: "Test3")
P.S: The desired output seems to be more appropriate for a description property of the Foo class.

Thanks for the answer I was hoping through this question to get a better understanding of how to use [for] parameters. But the while solution is the solution I would probably use with the following modifications
text = ""
var index = 0
while index < fooArray.count {
if let prop = fooArray[index] {
index += 1
let sep = fooArray[index]!
index += 1
text = text + prop + sep
} else {
index += 2
}
}

Related

Find the repeated sequence in the line that go in a row

Given a string of arbitrary length. I need to find 1 subsequences of identical characters that go in a row.
My function (there are two of them, but these are two parts of the same function) turned out to be complex and cumbersome and did not fit because of this. The function I need should be simple and not too long.
Example:
Input : str = "abcabc"
Output : abc
Input : str = "aa"
Output : a
Input : str = "abcbabcb"
Output : abcb
Input : str = "abcbca"
Output : bcbc
Input : str = "cbabc"
Output :
Input : str = "acbabc"
Output :
My unsuccessful function:
func findRepetition(_ p: String) -> [String:Int] {
var repDict: [String:Int] = [:]
var p = p
while p.count != 0 {
for i in 0...p.count-1 {
repDict[String(Array(p)[0..<i]), default: 0] += 1
}
p = String(p.dropFirst())
}
return repDict
}
var correctWords = [String]()
var wrongWords = [String]()
func getRepeats(_ p: String) -> Bool {
let p = p
var a = findRepetition(p)
for i in a {
var substring = String(Array(repeating: i.key, count: 2).joined())
if p.contains(substring) {
wrongWords.append(p)
return false
}
}
correctWords.append(p)
return true
}
I will be very grateful for your help!
Here's a solution using regular expression. I used a capture group that tries to match as many characters as possible such that the whole group repeats at least once.
import Foundation
func findRepetition(_ s: String) -> String? {
if s.isEmpty { return nil }
let pattern = "([a-z]+)\\1+"
let regex = try? NSRegularExpression(pattern: pattern, options: [])
if let match = regex?.firstMatch(in: s, options: [], range:
NSRange(location: 0, length: s.utf16.count)) {
let unitRange = match.range(at: 1)
return (s as NSString).substring(with: unitRange)
}
return nil
}
print(findRepetition("abcabc")) //prints abc
print(findRepetition("aa")) //prints a
print(findRepetition("abcbabcb")) //prints abcb
print(findRepetition("abcbca")) //prints bc
print(findRepetition("cbabc")) //prints nil
print(findRepetition("acbabc")) //prints nil
func findRepetitions(_ p : String) -> [String: Int]{
let half = p.count / 2 + 1
var result : [String : Int] = [:]
for i in 1..<half {
for j in 0...(p.count-i) {
let sub = (p as! NSString).substring(with: NSRange.init(location: j, length: i))
if let val = result[sub] {
result[sub] = val + 1
}else {
result[sub] = 1
}
}
}
return result
}
This is for finding repetitions of possible substrings in your string. Hope it can help
Here is a solution that is based on the Suffix Array Algorithm, that finds the longest substring that is repeated (contiguously):
func longestRepeatedSubstring(_ str: String) -> String {
let sortedSuffixIndices = str.indices.sorted { str[$0...] < str[$1...] }
let lcsArray = [0]
+
sortedSuffixIndices.indices.dropFirst().map { index in
let suffix1 = str[sortedSuffixIndices[index]...]
let suffix2 = str[sortedSuffixIndices[index - 1]...]
let commonPrefix = suffix1.commonPrefix(with: suffix2)
let count = commonPrefix.count
let repeated = suffix1.dropFirst(count).commonPrefix(with: commonPrefix)
return count == repeated.count ? count : 0
}
let maxRepeated = zip(sortedSuffixIndices.indices,lcsArray).max(by: { $0.1 < $1.1 })
if let tuple = maxRepeated, tuple.1 != 0 {
let suffix1 = str[sortedSuffixIndices[tuple.0 - 1]...]
let suffix2 = str[sortedSuffixIndices[tuple.0]...]
let longestRepeatedSubstring = suffix1.commonPrefix(with: suffix2)
return longestRepeatedSubstring
} else {
return ""
}
}
Here is an easy to understand tutorial about such an algorithm.
It works for these examples:
longestRepeatedSubstring("abcabc") //"abc"
longestRepeatedSubstring("aa") //"a"
longestRepeatedSubstring("abcbabcb") //"abcd"
longestRepeatedSubstring("abcbca") //"bcbc"
longestRepeatedSubstring("cbabc") //""
longestRepeatedSubstring("acbabc") //""
As well as these:
longestRepeatedSubstring("a😍ca😍c") //"a😍c"
longestRepeatedSubstring("Ab cdAb cd") //"Ab cd"
longestRepeatedSubstring("aabcbc") //"bc"
Benchmarks
Here is a benchmark that clearly shows that the Suffix Array algorithm is much faster than using a regular expression.
The result is:
Regular expression: 7.2 ms
Suffix Array : 0.1 ms

Reversing the order of a string value

I have the following function which reverses a string value's display order.
I'm new to Swift and I'm trying to understand it's logic. What is going on with the '!pleh' value that it turns into 'Help!' ?
Thanks
func reverse(_ s: String) -> String {
var str = ""
for character in s.characters {
str = "\(character)" + str
}
return str
}
print (reverse("!pleH"))
In swift 4.0, directly call reversed on a string will get the job done
let str = "abc"
String(str.reversed()) // This will give you cba
Commented inline,
func reverse(_ s: String) -> String {
var str = ""
//.characters gives the character view of the string passed. You can think of it as array of characters.
for character in s.characters {
str = "\(character)" + str
//This will help you understand the logic.
//!+""
//p+!
//l+p! ... goes this way
print ( str)
}
return str
}
print (reverse("!pleH"))
Note: If you don't want to use the reversed() in-built function, then you can use the following code.
One-liner using Higher-order function "Reduce" on the string.
extension String {
func reverse() -> String { return self.reduce("") { "\($1)" + $0 } }
}
The function reversed(_:) iterates over each Character in the supplied string s, and simply concenates them in the reverse order.
// 1st pass in the 'for' loop:
// # start of iteration: 'str' equals ""
// update value of 'str' according to
// str = aCharacterAsString + str = "!" + ""
// 2nd pass in the 'for' loop:
// # start of iteration: str equals "!"
// update value of 'str' according to
// str = anotherCharacterAsString + str = "p" + "!"
// 3rd pass in the 'for' loop:
// # start of iteration: str equals "p!"
// update value of 'str' according to
// str = yetAnotherCharacterAsString + str = "l" + "p!"
// and so on ...
// after end of 'for' loop: str == "help!"
// this value of 'str' is then return to callee (which prints it)
A much simpler approach would be using reversed() on the CharacterView of the String instance:
let str = "!pleH"
print(String(str.characters.reversed())) // Help!
Swift 4 You can use it directly in your string
let str = "!pleH"
print(String(str.reversed())) // Help!
var string = "My,playground and my Swift"
var reverseString = ""
for str in string {
reverseString.insert(str, at: reverseString.startIndex)
}
print(reverseString)
In Swift 4 - To reverse string ::
func reverse(string:String) -> String {
var reverse = ""
for char in string {
reverse = char.description + reverse
}
return reverse
}
Input :: reverse(string: "Ashish Chhabra")
Output :: arbahhC hsihsA
var strnew = "hello world new"
var reverseStr = ""
for each in strnew
{
reverseStr = String(each) + reverseStr
}
print(reverseStr)
Different variation of answer using Character and Index.
//Reverse String
func reverse(str:String)->String{
var chars = [Character]()
for i in (0...str.count).reversed() {
let index = str.index(str.startIndex, offsetBy: i)
chars.append(str[index])
}
return String(chars)
}
You can use below code-
let str = "My name is Kplin Cours"
var reverseString = ""
for i in 0..<str.count {
let index = str.index(str.startIndex, offsetBy: (str.count - 1) - i)
// print(String(str[index]))
reverseString = reverseString + String(str[index])
}
print(reverseString) //sruoC nilpK si eman yM
let string = readLine()!
var resultString = ""
for i in 1...string.count {
let index = string.index(string.endIndex, offsetBy: -i)
resultString.append(string[index])
}
print(resultString)
let inputstr = "ABCDEFGHIGKLMNPO"
var resultstr = "";
for strchar in inputstr {
resultstr = String(strchar) + resultstr
}
print("Result = ",resultstr)
Swift 5
extension String {
func invertedEntropy() -> String {
var word = [Character]()
for char in self {
word.insert(char, at: 0)
}
return String(word)
}
}
var palindrome = "TENET"
palindrome.invertedEntropy()
// "TENET"
Simple as that!
let str = "Hello, world!"
let reversed = String(str.reversed())
print(reversed)

Reverse Strings without using predefined functions

Pardon me as I am a newbie on this language.
Edit: Is there a way to reverse the position of a array element?
I am trying to create a function that test the given input if its a palindrome or not. I'm trying to avoid using functions using reversed()
let word = ["T","E","S","T"]
var temp = [String]()
let index_count = 3
for words in word{
var text:String = words
print(text)
temp.insert(text, atIndex:index_count)
index_count = index_count - 1
}
Your approach can be used to reverse an array. But you have to
insert each element of the original array at the start position
of the destination array (moving the other elements to the end):
// Swift 2.2:
let word = ["T", "E", "S", "T"]
var reversed = [String]()
for char in word {
reversed.insert(char, atIndex: 0)
}
print(reversed) // ["T", "S", "E", "T"]
// Swift 3:
let word = ["T", "E", "S", "T"]
var reversed = [String]()
for char in word {
reversed.insert(char, at: 0)
}
print(reversed) // ["T", "S", "E", "T"]
The same can be done on the characters of a string directly:
// Swift 2.2:
let word = "TEST"
var reversed = ""
for char in word.characters {
reversed.insert(char, atIndex: reversed.startIndex)
}
print(reversed) // "TSET"
// Swift 3:
let word = "TEST"
var reversed = ""
for char in word.characters {
reversed.insert(char, at: reversed.startIndex)
}
print(reversed)
Swift 5
extension String {
func invert() -> String {
var word = [Character]()
for char in self {
word.insert(char, at: 0)
}
return String(word)
}
}
var anadrome = "god"
anadrome.invert()
// "dog"
Here's my solution:
extension String {
func customReverse() -> String {
var chars = Array(self)
let count = chars.count
for i in 0 ..< count/2 {
chars.swapAt(i, count - 1 - i)
}
return String(chars)
}
}
let input = "abcdef"
let output = input.customReverse()
print(output)
You can try it here.
func reverse(_ str: String) -> String {
let arr = Array(str) // turn the string into an array of all of the letters
let reversed = ""
for char in arr {
reversed.insert(char, at: reversed.startIndex)
}
return reversed
}
To use it:
let word = "hola"
let wordReversed = reverse(word)
print(wordReversed) // prints aloh
Another solution for reversing:
var original : String = "Test"
var reversed : String = ""
var c = original.characters
for _ in 0..<c.count{
reversed.append(c.popLast()!)
}
It simply appends each element of the old string that is popped, starting at the last element and working towards the first
Solution 1
let word = "aabbaa"
let chars = word.characters
let half = chars.count / 2
let leftSide = Array(chars)[0..<half]
let rightSide = Array(chars.reverse())[0..<half]
let palindrome = leftSide == rightSide
Solution 2
var palindrome = true
let chars = Array(word.characters)
for i in 0 ..< (chars.count / 2) {
if chars[i] != chars[chars.count - 1 - i] {
palindrome = false
break
}
}
print(palindrome)
static func reverseString(str : String) {
var data = Array(str)
var i = 0// initial
var j = data.count // final
//either j or i for while , data.count/2 buz only half we need check
while j != data.count/2 {
print("befor i:\(i) j:\(j)" )
j = j-1
data.swapAt(i, j) // swapAt API avalible only for array in swift
i = i+1
}
print(String(data))
}
//Reverse String
let str = "Hello World"
var reverseStr = ""
for char in Array(str) {
print(char)
reverseStr.insert(char, at: str.startIndex)
}
print(reverseStr)

String.Index does not have a member named advancedBy

This is the swift code I have as an exercise from my class, I am re-writing it(as I am new to it).
At the position of
let end = starIndex.advancedBy(position), I am getting an error message of String.Index does not have a member named advancedBy I am not sure what this means or how to correct it just yet, many thanks for any help in understanding what I am doing wrong.
//: Playground - noun: a place where people can play
import UIKit
var str : String = "Hello, playground"
str
let strFix = " Can not change"
str = "Good bye"
//strFix = "Testing"
var str2 = str + "F"
var townName = "NeverLand"
let population = 30000
let numOfStopLight : Int = 10
print("\(townName) has populations of \(population)
and has \ (numOfStopLight) Stop Lights")
for c in townName.unicodeScalars
{
print ("\(c)")
}
let starIndex = str.startIndex
let position = 3
let end = starIndex.advancedBy(position)
let charAt = str[end]
let range = starIndex...end
str[range]
let aChar: Character = "\u{1F60E}"
str.append(aChar)
let dollarSign = "\u{24}" //$, Unicode scalar U+0024
let blackHeart = "\u{2665}" // , Unicode scalar U+2665
let sparklingHeart = "\u{1F496}" // , Unicod scalar U=1F496
str.append(Character(sparklingHeart))
for c in str.characters
{
print("\(c)")
}
for c in str.unicodeScalars
{
print("\(c.value)")
}
/////////////////////
struct Car
{
var make : String = ""
var price : Double = 0.0
let color : String = "RED"
func getMske()->String
{
return make
}
mutating func setMake (m: String)
{
make = m
}
func showCar()->String
{
return "Make:\(make) Price=\(price) Color= \(color)"
}
}
//let us make some Car
var myCar = Car(make: "Nissan", price: 45000.0)
print(myCar.showCar())
myCar.setMake("Nissan2016")
print (myCar.showCar())
//show pass value
func doChange (var c: Car)
{
c.setMake("Toyota")
print (c.showCar())
}
doChange(myCar)
print (myCar.showCar())
//let us have a class
class Xcar
{
var make :String = ""
var price : Double = 0.0
var color : String = ""
init(m :String, p: Double, c: String)
{
self.make = m
self.price = p
self.color = c
}
func setMake(m: String)
{
self.make = m
}
func showXCar()->String
{
return "Make: \(make) Price=\(price) Color=\(color)"
}
}
func doChangex( c:Xcar)
{
c.setMake("BMW")
print("\(c.showXCar())")
}
var hisCar = Xcar(m: "Fiat", p: 15000.0, c: "Blue")
print ("\(hisCar.showXCar())")
doChangex(hisCar)
//notice the function did change
//because it was a class
print ("\(hisCar.showXCar())")
//What is optional
let a: Float?
let b: Float?
let c :Float?
a = 10
b = 20
c = 5
//this is implicit unrap
let ave = (a! + b! + c!)/3.0
if let x = a, y = b, z = c
{
print ("\((x + y + z) / 3.0)")
}
else
{
print ("missing value...")
}
// note Array, Double, Float, Int and Dictionary are all struct
var intArray = [Int] ()
intArray.append(50)
intArray.append(100)
intArray.append(600)
intArray.maxElement()
intArray.capacity
intArray.count
intArray.description
intArray.dropFirst()
intArray.first
intArray.description
intArray.removeFirst()
intArray.description
for c in intArray
{
print("\(c)")
}
var dict = [Int :String ] ()
dict = [235 :"Jack", 100: "Joe", 60: "Lisa"]
dict.description
for(key, value) in dict
{
print ("\(key)")
}
var name = dict[235]
Try doing it in one line
let startIndex = str.startIndex.advancedBy(3)
The code has a number of issues and we don't know what the expected output is, but here are a few items:
The line with just 'str' on it should be removed.
You may be running an older version of Xcode or OS (or both). This is Swift2 and you should be using Xcode 7 or higher.
Also, there are 'var' that should be 'let' (hisCar) and there are a number of unused variables.
The good news is once you fix the issues that, the code runs correctly (as far as I can tell)

Proper way to concatenate optional swift strings?

struct Person {
var firstName: String?
var lastName: String?
}
Now I want to construct a fullName string that contains either just their first or last name (if that's all that is available), or if we have both, their first and last name with a space in the middle.
var fullName: String?
if let first = person.firstName {
fullName = first
}
if let last = person.lastName {
if fullName == nil {
fullName = last
} else {
fullName += " " + last
}
}
or
var fullName = ""
if let first = person.firstName {
fullName = first
}
if let last = person.lastName {
fullName += fullName.count > 0 ? (" " + last) : last
}
Are we just supposed to nest if let's? Nil coalescing seems appropriate but I can't think of how to apply it in this scenario. I can't help but feeling like I'm doing optional string concatenation in an overly complicated way.
compactMap would work well here, combined with .joined(separator:):
let f: String? = "jo"
let l: String? = "smith"
[f,l] // "jo smith"
.compactMap { $0 }
.joined(separator: " ")
It doesn't put the space between if one is nil:
let n: String? = nil
[f,n] // "jo"
.compactMap { $0 }
.joined(separator: " ")
Somewhere, I believe in the swift book, I ran into this pattern, from when before you could have multiple lets in a single if:
class User {
var lastName : String?
var firstName : String?
var fullName : String {
switch (firstName, lastName) {
case (.Some, .Some):
return firstName! + " " + lastName!
case (.None, .Some):
return lastName!
case (.Some, .None):
return firstName!
default:
return ""
}
}
init(lastName:String?, firstName:String?) {
self.lastName = lastName
self.firstName = firstName
}
}
User(lastName: nil, firstName: "first").fullName // -> "first"
User(lastName: "last", firstName: nil).fullName // -> "last"
User(lastName: nil, firstName: nil).fullName // -> ""
User(lastName: "last", firstName: "first").fullName // -> "first last"
An even briefer solution, given swift 3.0:
var fullName : String {
return [ firstName, lastName ].flatMap({$0}).joined(separator:" ")
}
Sometimes simple is best:
let first = p.first ?? ""
let last = p.last ?? ""
let both = !first.isEmpty && !last.isEmpty
let full = first + (both ? " " : "") + last
This works if there is no first or last, if there is a first but no last, if there is a last but no first, and if there are both a first and a last. I can't think of any other cases.
Here's an idiomatic incorporation of that idea into a calculated variable; as an extra benefit, I've allowed full to be nil just in case both the other names are nil:
struct Person {
var first : String?
var last : String?
var full : String? {
if first == nil && last == nil { return nil }
let fi = p.first ?? ""
let la = p.last ?? ""
let both = !fi.isEmpty && !la.isEmpty
return fi + (both ? " " : "") + la
}
}
Here is an alternative method:
let name =
(person.first != nil && person.last != nil) ?
person.first! + " " + person.last! :
person.first ?? person.last!
For those who are want to check nil and "" value as well you can do something like this:
var a: String? = nil
let b = "first value"
let c: String? = nil
let d = ""
let e = "second value"
var result = [a,b,c,d,e].compactMap{ $0 }.filter { $0 != "" }.joined(separator:", ")
print(result)
//first value, second value
I like oisdk's approach but I didn't like the empty string if both were nil. I would rather have nil.
func add(a a: String?, b: String?, separator: String = " ") -> String? {
let results = [a, b].flatMap {$0}
guard results.count > 0 else { return nil }
return results.joinWithSeparator(separator)
}
What oisdk answered was great, but I needed something very specific along the lines of the OP's original question.
Writing for Swift 4.x, I created this extension which works well when populating other strings, such as text labels. I have also updated it to include a function for handling an array if needed.
extension String {
static func combine(first: String?, second: String?) -> String {
return [first, second].compactMap{ $0 }.joined(separator: " ")
}
static func combine(strings: [String?]) -> String {
return strings.compactMap { $0 }.joined(separator: " ")
}
}
An example of this populating a text label with two optional strings:
print(String.combine(first: "First", second: "Last")) // "First Last"
print(String.combine(first: "First", second: nil)) // "First"
print(String.combine(first: nil, second: "Last")) // "Last"
If you have an array of optional strings, you can call the array function:
print(String.combine(strings: ["I", "Have", nil, "A", "String", nil, "Here"]))
// "I Have A String Here"
for swift 4
let name: String? = "Foo"
let surname: String? = "Bar"
let fullname = (name ?? "") + " " + (surname ?? "")
print(fullname)
// Foo Bar
func getSingleValue(_ value: String?..., seperator: String = " ") -> String? {
return value.reduce("") {
($0) + seperator + ($1 ?? "")
}.trimmingCharacters(in: CharacterSet(charactersIn: seperator) )
}
It's too bad that there isn't more support for operators on the Optional enum, but I overloaded the standard concatenation operator (+) like this:
func +<T: StringProtocol>(lhs: Optional<T>, rhs: Optional<T>) -> String {
return [lhs, rhs].compactMap({ $0 }).joined()
}
Then you can use it like this:
let first: String? = "first"
let last: String? = nil
first + first // "firstfirst"
first + last // "first"
last + first // "first"
last + last // ""
Add-On:
Consider you have Struct:
struct Person {
var firstName: String?
var lastName: String?
}
you can use the CustomStringConvertible
extension Person: CustomStringConvertible {
var description: String {
[firstName, lastName].compactMap { $0 }.joined(separator: " ")
}
}
let person1 = Person(firstName "Jeba", lastName: "Moses")
print(person1) // Prints "Jeba Moses"
let person2 = Person(firstName: "Ebenrick", lastName: nil)
print(person2) // Prints "Ebenrick"