Extracting substring from String in iOS Swift - swift

I have created some strings as below:
let firstname = ""
let lastname = ""
let myInfo = "Dana<fname>Dana<lname>CEO<occupation>0123456<hp>01234567<wp>dana#gmail.com<email>"
I want to extract certain parts out of that string. For example, I want to assign the part before <fname> to the firstname variable, and the part before <lname> to the lastname variable.

Just a fast idea, probably there is some simpler way to do that:
let myInfo = "Dana<fname>Dana<lname>CEO<occupation>0123456<hp>01234567<wp>dana#gmail.com<email>"
let components = myInfo.components(separatedBy: CharacterSet(charactersIn: "<>"))
let values = components.enumerated().filter { $0.offset % 2 == 0 }.map { $0.element }
let keys = components.enumerated().filter { $0.offset % 2 == 1 }.map { $0.element }
var namedValues: [String: String] = [:]
for i in keys.indices {
namedValues[keys[i]] = values[i]
}
print(namedValues)
Then just:
let firstName = namedValues["fname"]
let lastName = namedValues["lname"]

You can replace occurrences of string with another string to do it.
Example: Any way to replace characters on Swift String?

Related

Swift - Reading different types of values from a dictionary

Presently the code that I have is reading all the values as String. However at times when an integer or decimal values are present, it gets read as nil.
Present code:
let fieldName = String(arr[0])
var res = dict[fieldName.uppercased()] as? String
if res == nil {
res = dict[fieldName.lowercased()] as? String
}
url = url.replacingOccurrences(of: testString, with: res?.addingPercentEncoding(withAllowedCharacters: allowedCharSet) ?? "")
There are times when "dict[fieldName.uppercased()]" returns value such as 3 or 40.4, but value in my res object is nil since I am expecting a string.
How can I get read different types of values and update the occurrences in my url?
Code that I tried:
let fieldName = String(arr[0])
var res = dict[fieldName.uppercased()] as? AnyObject
if res == nil {
res = dict[fieldName.lowercased()] as? AnyObject
}
url = url.replacingOccurrences(of: testString, with: res?.addingPercentEncoding(withAllowedCharacters: allowedCharSet) ?? "")
With this I am getting errors while replacing the occurrences since "addingPercentEncoding" only works on String.
So I check the class of res object and if it is not String, I try doing the below, but getting error since res is of type AnyObject and if that's not present, I try to replace it with empty string.
url = url.replacingOccurrences(of: testString, with: res ?? "" as String)
There is a common type of String, Int and Double: CustomStringConvertible
Conditional downcast the value to CustomStringConvertible and get a string with String Interpolation
let fieldName = String(arr[0])
if let stringConvertible = dict[fieldName.uppercased()] as? CustomStringConvertible {
url = url.replacingOccurrences(of: testString, with: "\(stringConvertible)".addingPercentEncoding(withAllowedCharacters: allowedCharSet
}
You should separate the "get the dictionary value" part and the "convert it to my desired type" part. And you should check the type of the value you got from the dictionary using if let statements.
let value = dict[fieldName.uppercased()] ?? dict[fieldName.lowercased()]
if let string = value as? String {
url = url.replacingOccurrences(of: testString, with: string.addingPercentEncoding(withAllowedCharacters: allowedCharSet) ?? "")
} else if let double = value as? Double {
url = url.replacingOccurrences(of: testString, with: "\(double)".addingPercentEncoding(withAllowedCharacters: allowedCharSet) ?? "")
} else if let integer = value as? Int {
url = url.replacingOccurrences(of: testString, with: "\(integer)".addingPercentEncoding(withAllowedCharacters: allowedCharSet) ?? "")
} else {
// value is nil, or is none of the types above. You decide what you want to do here
}

Iterate through the array of Dictionary and separate the name for status true

I want to iterate through this array and separate out the names that have status true.
var array = [["name":"joe", "status":false ],["name":"will", "status":false],["name":"smith" , "status":false]]
This should do the trick.
var array = [["name":"joe", "status":true ],["name":"will", "status":true],["name":"smith" , "status":false]]
let filteredDictionary = array.filter( { $0["status"] as? Bool ?? false } )
var names = [String]()
for dictionary in filteredDictionary {
if let nameFound = dictionary["name"] as? String {
names.append(nameFound)
}
}
I recommend that you use a struct to store the values instead of a dictionary. Something like this.
struct Person {
var name: String
var status: Bool
}
If you have it like this in a Person array, it becomes less complicated as dictionary value optional handling can be avoided.
var personArray = [Person(name: "joe", status: true), Person(name: "will", status: false)
let names = personArray.filter( {$0.status} ).map( {$0.name} )
You can use a simple filter is you want to keep both the status and name, otherwise use compactMap is you just want to keep the name.
let statuses = [["name":"joe", "status":true ],["name":"will", "status":false],["name":"smith" , "status":false]]
let trueStatuses = statuses.filter({$0["status"] as? Bool == true}) // [["name": "joe", "status": true]]
let namesWithTrueStatus = statuses.compactMap{$0["status"] as? Bool == true ? $0["name"] as? String : nil} //["joe"]

How to split string into Int:String Dictionary

So I'm trying to split a string that would look like this:
let Ingredients = "1:egg,4:cheese,2:flour,50:sugar"
and I'm attempting to get a dictionary output like this
var decipheredIngredients : [Int:String] = [
1 : "egg",
4 : "cheese",
2 : "flour",
50 : "sugar"
]
Here is the code that I am attempting this with
func decipherIngredients(input: String) -> [String:Int]{
let splitStringArray = input.split(separator: ",")
var decipheredIngredients : [String:Int] = [:]
for _ in splitStringArray {
decipheredIngredients.append(splitStringArray.split(separator: ":"))
}
return decipheredIngredients
}
When I try this I get an error saying I can't append to the dictionary. I've tried other methods like this:
func decipherIngredients(input: String) -> [String.SubSequence]{
let splitStringArray = input.split(separator: ",")
return splitStringArray
}
let newThing = decipherIngredients(input: "1:egg,4:cheese,2:flour,50:sugar").split(separator: ":")
print(newThing)
but I get this as the output of the function
[ArraySlice(["1:egg", "4:cheese", "2:flour", "50:sugar"])]
An alternative approach using Swift 4 and functional programming:
let ingredients = "1:egg,4:cheese,2:flour,50:sugar"
let decipheredIngredients = ingredients.split(separator: ",").reduce(into: [Int: String]()) {
let ingredient = $1.split(separator: ":")
if let first = ingredient.first, let key = Int(first), let value = ingredient.last {
$0[key] = String(value)
}
}
print(decipheredIngredients)
Swift 3
try this, assuming you want dictionary keys of type Int and values of type String
func decipherIngredients(_ input: String) -> [Int:String] {
var decipheredIngredients : [Int:String] = [:]
let keyValueArray = input.components(separatedBy: ",")
for keyValue in keyValueArray {
let components = keyValue.components(separatedBy: ":")
decipheredIngredients[Int(components[0])!] = components[1]
}
return decipheredIngredients
}

How to check last 3 character in string contain numbers in Swift?

For a given String instance, I want to check whether the last three characters are numeric characters (0, 1, 2, ..., 9) or not.
For example, the string
let str1 = "SACH092"
should return true for such a query, whereas e.g.
let str2 = "SACHA92"
should return false for the query.
I am using Xcode 7.3.1.
(As pointed out by #NiravD, for pre Swift 3, use where to join parts of multi-clause conditions. For Swift 3, parts of multi-clause conditions are simply joined by ,. For both methods below, both Swift 2.2 and 3 versions are included)
Use pattern matching for numeric characters "0"..."9"
Swift 2.2
extension String {
var lastThreeLettersAreNumbers: Bool {
if case let chars = characters.suffix(3) where chars.count > 2 {
let numbersPattern = Character("0")..."9"
return chars.reduce(true) { $0 && (numbersPattern ~= $1) }
}
return false
}
}
Swift 3
extension String {
var lastThreeLettersAreNumbers: Bool {
if case let chars = characters.suffix(3), chars.count > 2 {
let numbersPattern = Character("0")..."9"
return chars.reduce(true) { $0 && (numbersPattern ~= $1) }
}
return false
}
}
/* example usage, common for both Swift 2.2/3 version */
let str1 = "SACH092"
let str2 = "SACH0B2"
print(str1.lastThreeLettersAreNumbers) // true
print(str2.lastThreeLettersAreNumbers) // false
Make use of nil-return Int by String initializer, with flatMap
You can make use of the fact that the Int by String initializer returns nil for strings that cannot be represented as integers.
Swift 2.2
extension String {
var lastThreeLettersAreNumbers: Bool {
if case let chars = characters.suffix(3) where chars.count > 2 {
return chars.flatMap{Int(String($0))}.count == 3
}
return false
}
}
Swift 3
extension String {
var lastThreeLettersAreNumbers: Bool {
if case let chars = characters.suffix(3), chars.count > 2 {
return chars.flatMap{Int(String($0))}.count == 3
}
return false
}
}
/* example usage, common for both Swift 2.2/3 version */
let str1 = "SACH092"
let str2 = "SACH0B2"
print(str1.lastThreeLettersAreNumbers) // true
print(str2.lastThreeLettersAreNumbers) // false
For getting last 3 characters,
let exampleString = "SACH092"
let last3Char = exampleString.substringFromIndex(exampleString.endIndex.advancedBy(-3))
Check if last3Char contains all Digits,
let badCharacters = NSCharacterSet.decimalDigitCharacterSet().invertedSet
if last3Char.rangeOfCharacterFromSet(badCharacters) == nil {
print("String contains all digits")
} else {
print("String contains non-digit characters")
}
The best way to achieve string control is to use Regular Expressions.
For you :
var str = "SACH092"
let pattern = "^.*[0-9]{3,3}$"
let regexp = try! NSRegularExpression(pattern: pattern, options: [])
let matches = regexp.matches(in: str, options: [], range: NSMakeRange(0, str.characters.count))
print("End with 3 numbers : \(matches.count > 0)")
You can use like this
let s : NSString = "SACH092"
let trimmedString: String = (s as NSString).substringFromIndex(max(s.length-3,0))
print(trimmedString.isNumeric) // return true or false
//Make Extension of String
extension String {
var isNumeric: Bool {
let nums: Set<Character> = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
return Set(self.characters).isSubsetOf(nums)
}
}
To get the last three letters of a string
let oldString = "yourString"
let newString = a.substringFromIndex(a.endIndex.advancedBy(-3))
To check those characters are numbers,
func isNumber(num: String) -> Bool {
let numberCharacters = NSCharacterSet.decimalDigitCharacterSet().invertedSet
return !num.isEmpty && num.rangeOfCharacterFromSet(numberCharacters) == nil
}

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)