This question already has an answer here:
How to make a function that takes a string and replace some letters with another symbols
(1 answer)
Closed 3 months ago.
I need to create function that will change several elements in 1 string
Without declaring this as a function - it work properly, but in the function it changing only 1 random char
can u help me pls
import Foundation
func makeItCool(_ string: String) -> String {
var newCoolString = string
let replaces = [
"a" : "#",
"o" : "0",
"t" : "+",
"i" : "1",
"s" : "$",
]
for (key, value) in replaces {
newCoolString = string.lowercased().replacingOccurrences(of: key, with: value)
}
return newCoolString
}
print(makeItCool("Swift is Awesame"))
//sw1ft 1s awesame
Working code
var string = "Swift is Awesaome"
let replaces = [
"a" : "#",
"o" : "0",
"t" : "+",
"i" : "1",
"s" : "$",
]
for (key, value) in replaces {
string = string.lowercased().replacingOccurrences(of: key, with: value)
}
print(string)
// $w1f+ 1$ #we$#0me
In the working example you are iteratively updating the same string, so the effects of the replacements are cumulative.
In the failing version you are always applying the transformation to the original method parameter string so each time through the for loop you will overwrite the previous changes, and will just return the changes from the final iteration.
I'm sure you meant to write:
for (key, value) in replaces {
newCoolString = newCoolString.lowercased().replacingOccurrences(of: key, with: value)
}
although a more efficient approach would be to
let newCoolString = string.lowercased()
//...
for (key, value) in replaces {
newCoolString = newCoolString.replacingOccurrences(of: key, with: value)
}
func makeItCool(_ string: String) -> String {
var newCoolString = string.lowercased()
let replaces = [
"a" : "#",
"o" : "0",
"t" : "+",
"i" : "1",
"s" : "$",
]
for (key, value) in replaces {
newCoolString = newCoolString.replacingOccurrences(of: key, with: value)
}
return newCoolString
}
print(makeItCool("Swift is Awesame"))
I think the problem is in this line
newCoolString = string.lowercased().replacingOccurrences(of: key, with: value)
You should replace occurrences in newCoolString like this:
newCoolString = newCoolString.lowercased().replacingOccurrences(of: key, with: value)
Related
I have strings like
"\U0aac\U0ab9\U0ac1\U0ab5\U0a9a\U0aa8",
"\U0a97\U0ac1\U0ab8\U0acd\U0ab8\U0acb",
"\U0aa6\U0abe\U0ab5\U0acb",
"\U0a96\U0a82\U0aa1"
But I want to split this strings by unicode character
I dont know hot to do. I know components seprated by function but it's no use here.
\nAny help would be apperiaciated
If the strings you're getting really contain \U characters, you need to parse them manually and extract the unicode scalar values. Something like this:
let strings = [
"\\U0aac\\U0ab9\\U0ac1\\U0ab5\\U0a9a\\U0aa8",
"\\U0a97\\U0ac1\\U0ab8\\U0acd\\U0ab8\\U0acb",
"\\U0aa6\\U0abe\\U0ab5\\U0acb",
"\\U0a96\\U0a82\\U0aa1"
]
for str in strings {
let chars = str.components(separatedBy: "\\U")
var string = ""
for ch in chars {
if let val = Int(ch, radix: 16), let uni = Unicode.Scalar(val) {
string.unicodeScalars.append(uni)
}
}
print(string)
}
You can map your array, split its elements at non hexa digit values, compact map them into UInt32 values, initializate unicode scalars with them and map the resulting elements of your array into a UnicodeScalarView and init a new string with it:
let arr = [
#"\U0aac\U0ab9\U0ac1\U0ab5\U0a9a\U0aa8"#,
#"\U0a97\U0ac1\U0ab8\U0acd\U0ab8\U0acb"#,
#"\U0aa6\U0abe\U0ab5\U0acb"#,
#"\U0a96\U0a82\U0aa1"#]
let strings = arr.map {
$0.split { !$0.isHexDigit }
.compactMap { UInt32($0, radix: 16) }
.compactMap(Unicode.Scalar.init)
}.map { String(String.UnicodeScalarView($0)) }
print(strings)
This will print
["બહુવચન", "ગુસ્સો", "દાવો", "ખંડ"]
So, the string that comes back already has the "\" because in order to use components you'd need to have an additional escaping "\" so that you'd be able to do:
var listofCodes = ["\\U0aac\\U0ab9\\U0ac1\\U0ab5\\U0a9a\\U0aa8", "\\U0aac\\U0ab9\\U0ac1\\U0ab5\\U0a9a\\U0aa8"]
var unicodeArray :[String] = []
listofCodes.forEach { string in
unicodeArray
.append(contentsOf: string.components(separatedBy: "\\"))
unicodeArray.removeAll(where: {value in value == ""})
}
print(unicodeArray)
I will revise this answer once you specify how you are obtaining these strings, as is I get a non-valid string error from the start.
I am trying to parse the following json and want to retrieve the "key" of a dictionary whose value matches with the given value.
{ "OuterArrayHolder" :
[
{
"dictDynamicKey" : ["dynamicValue1", "dynamicValue2", "dynamicValue3"]
},
{
"dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]
},
]
}
[Note: Here in above json, all the keys and values are dynamic except "OuterArrayHolder".]
I have implemented it in a non-Swifty way and currently getting the expected output, but I am not getting how to accomplish the same behaviour using swift's higher-order functions.
Input : "dynamicValue2"
Expected output : "dictDynamicKey"
Current solution:
let inputValue = "dynamicValue2"
if !outerArrayHolder.isEmpty {
for dynamicDict in outerArrayHolder {
for (key, value) in dynamicDict {
if value.empty || !value.contains(inputValue) {
continue
} else {
//here if inputValue matches in contianed array (value is array in dictionary) then I want to use its "repective key" for further businisess logic.
}
}
}
}
I want to reduce these two for loops and want to use higher-order functions to achieve the exact behavior, Any help in this regard is really appreciated.
Can we convert your algorithm to a functional style? Yes. Is it a good idea? Probably not in this case. But here's how.
You didn't give any type information, so I'll use this type:
let outerArrayHolder: [[String: Any]] = [
[
"dictDynamicKey": ["dynamicValue1", "dynamicValue2", "dynamicValue3"]
],
[
"dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]
],
]
And you want to find the key corresponding to the array that contains inputValue:
let inputValue = "dynamicValue2"
The functional strategy is to map each dictionary in outerArrayHolder to its first key that has a matching value. If a dictionary has no such key, the dictionary is mapped to nil. Then we throw away the nils and take the first remaining value.
We can do it with filter, as requested:
let key = outerArrayHolder.lazy
.compactMap {
$0.lazy
.filter { ($0.value as? [String])?.contains(inputValue) ?? false }
.map { $0.key }
.first }
.first
But we can save a lazy and a first using first(where:):
let key = outerArrayHolder.lazy
.compactMap({
$0
.first(where: { ($0.value as? [String])?.contains(inputValue) ?? false })
.map { $0.key }
}).first
I don't see what this has to do with higher-order functions. If the outer key is known, I would simply write
// just building your structure
let d1 = ["dictDynamicKey" : ["dynamicValue1", "dynamicValue2", "dynamicValue3"]]
let d2 = ["dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]]
let d = ["OuterArrayHolder" : [d1, d2]]
// this is the actual code:
func find(_ target:String) -> String? {
for dict in d["OuterArrayHolder"]! {
for (k,v) in dict {
if v.contains(target) {return k}
}
}
return nil
}
That's just what you're doing, only it's clean.
There's no higher order function that does precisely what you're looking for. The closest is first(where:), but the problem is that the result is just Bool, and you don't have a way to cleanly fish out data related to the found case.
You could write something like:
extension Sequence {
func findFirst<T>(where predicate: (Element) throws -> T?) rethrows -> T? {
for element in self {
if let result = try predicate(element) {
return result
}
}
return nil
}
}
and then use it like:
let dictionaries = [
[
"dictDynamicKey" : ["dynamicValue1", "dynamicValue2", "dynamicValue3"]
],
[
"dictAnotherDynamicKey" : ["dynamicValue4", "dynamicValue5", "dynamicValue6"]
],
]
let desiredValue = "dynamicValue2"
extension Sequence {
func findFirst<T>(where predicate: (Element) throws -> T?) rethrows -> T? {
for element in self {
if let result = try predicate(element) {
return result
}
}
return nil
}
}
let result = dictionaries.findFirst(where: { dict in
dict.findFirst(where: { key, values in
values.contains(desiredValue) ? key : nil
})
})
print(result as Any) // => Optional("dictDynamicKey")
But it's probably more complexity than it's probably worth. I would recommend Matt's solution.
Scaled solution
You haven't clarified on this, but I suspect that you probably need to do this a bunch of times. In that case, linear searching through this gets really slow. By searching for keys by their values, you're not taking advantage of the key benefit of dictionaries: constant-time access to a value by its key. Your code is:
Linear-searching through the array of dictionaries, introduces an O(dictionaries.count) factor
For each dict in the array in #1, linear-searching through the key/value pairs, which introduces a O(dict.count) factor
For each key/value pair in the dict in #2, linear-searching through array of values, which introduces a O(valueArray.count) factor.
The total time complexity multiplies up to O(dictionaries.count * averageDict.count * averageValueArray.count), which gets really slow really quick.
Instead, you can spend some compute cost up-front, to create a new data structure that is better able to service the kinds of queries you want to run on it. In this case, you can "invert" a dictionary.
extension Dictionary {
func inverted<T>() -> [T: Key] where Dictionary.Value == [T] {
let invertedKeyValuePairs = self
.lazy
.flatMap { oldKey, oldValues in
oldValues.map { oldValue in (key: oldValue, value: oldKey) as (T, Key) }
}
return Dictionary<T, Key>(uniqueKeysWithValues: invertedKeyValuePairs)
}
}
// Example usage:
let valuesByKeys = [
"a": [1, 2, 3],
"b": [4, 5, 6]
]
let keysPerValue = valuesByKeys.inverted()
keysPerValue.forEach { key, value in print("key: \(key), value: \(value)") }
// Which results in:
// key: 3, value: a
// key: 4, value: b
// key: 5, value: b
// key: 1, value: a
// key: 6, value: b
// key: 2, value: a
Given such an inverted implementation, you can invert each dict of your input set, and merge them all together:
let invertedDictionary = Dictionary(uniqueKeysWithValues: dictionaries.flatMap { $0.inverted() })
invertedDictionary.forEach { key, value in print("key: \(key), value: \(value)") }
// Result:
key: dynamicValue6, value: dictAnotherDynamicKey
key: dynamicValue1, value: dictDynamicKey
key: dynamicValue2, value: dictDynamicKey
key: dynamicValue3, value: dictDynamicKey
key: dynamicValue4, value: dictAnotherDynamicKey
key: dynamicValue5, value: dictAnotherDynamicKey
You can store and share this dictionary, which can give constant time (O(1)) access to the key that was associated with any desired value:
print(invertedDictionary[desiredValue] as Any) // => Optional("dictDynamicKey")
This question already has answers here:
Swift Regex for extracting words between parenthesis
(2 answers)
Closed 4 years ago.
Example String -
exampleString ( 123 )
I want two parts from the above string -
1-) exampleString by splitting
2-) 123 by splitting and then removing brackets and two spaces, one at each end
More specifically, I want to know how can I extract a string between
two brackets ( )
How to achieve that in swift 4.2?
Thanks
There is a solution below, it will extract any string between brackets into an array without brackets, it also gives you string without element as word as an array element:
var myString = "exampleString ( 123 ) "
//creates result array from String by separating elements by space
var result = myString.split(separator: " ")
//filters array to remove '(' and ')'
result = result.filter { $0 != "(" && $0 != ")" }
print(result)
Then if you want to build a String back from result array with string elements, do the following:
var resultString = result.joined(separator: " ")
print(resultString)
Might not be ideal, but it might be useful for you.
I found this beautiful String Extension for slicing a string. It is answered here -
Slice String Extension
extension String {
func slice(from: String, to: String) -> String? {
return (range(of: from)?.upperBound).flatMap { substringFrom in
(range(of: to, range: substringFrom..<endIndex)?.lowerBound).map { substringTo in
String(self[substringFrom..<substringTo])
}
}
}
}
Here I can simply get a substring like
let str = "exampleString ( 123 )"
print(str.slice(from: "( ", to: " )"))
How can I remove a key from a dictionary where the value is X? I need a dictionary.removeKeyForValue(...) function.
I'd like to optimise the following code. I have a text which is associated with a certain category and a dictionary that associates all keywords to categories. Although my text is already categorised I'd like check whether it should fall into a different category.
let text = "he said hello and then ran away" // This is taken from the "activity" category
// Dictionary associating keywords to categories
let categoryRules = ["hi" : "greeting", "hello" : "greeting", "jogging" : "activity", "joy" : "feeling"]
let keywords = Array(categoryRules.keys)
// Make out of text an Array of words.
let textWordArray = text.lowercaseString.characters.split{$0 == " "}.map(String.init)
// I SHOULDN'T HAVE TO GO THROUGH THE KEYS ASSOCIATED WITH "ACTIVITY" BECAUSE TEXT IS ALREADY IN IT.
for keyword in keywords {
// If text contains the rule
if let index = textWordArray.indexOf(keyword) {
// Get the associated category
if let category = categoryRules[keyword] {
print("The text should fall into the category of \(category)")
break
}
}
}
To remove all keys from a dictionary with a certain value, you can use a for loop with where clause to select the keys to remove and then assign nil to remove them:
var categoryRules = ["hi" : "greeting", "hello" : "greeting", "jogging" : "activity", "joy" : "feeling"]
for (key, value) in categoryRules where value == "greeting" {
categoryRules[key] = nil
}
print(categoryRules) // ["jogging": "activity", "joy": "feeling"]
You can add removeKeysForValue by adding an extension to Dictionary that works for values that are Equatable (can be compared with ==):
extension Dictionary where Value: Equatable {
mutating func removeKeysForValue(value: Value) {
for (key, val) in self where val == value {
self[key] = nil
}
}
}
var categoryRules = ["hi" : "greeting", "hello" : "greeting", "jogging" : "activity", "joy" : "feeling"]
categoryRules.removeKeysForValue("greeting")
print(categoryRules) // ["jogging": "activity", "joy": "feeling"]
Here's an alternate solution that I find elegant which uses filters.
var categoryRules = ["hi" : "greeting", "hello" : "greeting", "jogging" : "activity", "joy" : "feeling"]
let keysToRemove = dict.keys.filter { dict[$0]! == "greeting" }
for key in keysToRemove {
dict.removeValueForKey(key)
}
// categoryRules = ["jogging": "activity", "joy": "feeling"]
keysToRemove will have "hi" and "hello" because they matched the given filter of having the value "greeting".
Edit:
OP mentioned that he wanted a dictionary.removeKeyForValue() function. You can create an extension with the above code to increase readability and avoid code duplication if you plan on doing this action often.
Example:
extension Dictionary {
mutating func removeKeysForValue(value: NSObject) {
let keysToRemove = self.keys.filter { self[$0]! as! NSObject == value }
for key in keysToRemove {
self.removeValueForKey(key)
}
}
}
var dict = ["hi" : "greeting", "hello" : "greeting", "jogging" : "activity", "joy" : "feeling"]
dict.removeKeysForValue("greeting") // dict = ["jogging": "activity", "joy": "feeling"]
Since it will take O(n) time to remove keys from the dictionary (where n is the size of the dictionary) it might be a better idea to create a list of dictionaries (instead of just one.) Each dictionary would exclude the necessary elements. So when looking up values out of the dictionary you just have to choose the correct dictionary.
This will have better performance then removing keys from the dictionary at runtime. Especially when doing lookups multiple times in loops.
I have Swift dictionary:
private var params = [String : AnyObject]()
This contains query items like:
"lat" = "40"
"lon" = "100"
I would like to map this dictionary to NSURLQueryItem array. I want to make it "swifty":
params.map{NSURLQueryItem.init}
But I get an error. Even if I change the map to be [String:String?]. I know I can do something like this elegant one-liner. Can anybody tell how?
You just need to add a failable initializer to NSURLQueryItem that receives a tuple like this (String, AnyObject)
extension NSURLQueryItem {
convenience init?(tuple: (String, AnyObject)) {
guard let value = tuple.1 as? String else { return nil }
self.init(name: tuple.0, value: value)
}
}
That's it!
let params: [String:AnyObject] = ["lat": "40", "lon": "100"]
let queryItems = params.flatMap(NSURLQueryItem.init)
Does your value for the dictionary need to be an optional? In a dictionary, when you assign its key as nil, the entry is deleted.
var params = [String:String?]()
params["lat"] = "40"
params["lon"] = "100"
params["key"] = "hey"
print(params) //result: ["lat": Optional("40"), "lon": Optional("100"), "key": Optional("hey")]
params["key"] = nil
print(params) //result: ["lat": Optional("40"), "lon": Optional("100")]
I suggest using a non optional-value dictionary. I have successfully written the code below:
import UIKit
var params = [String:String]()
params["lat"] = "40"
params["lon"] = "100"
let nsurl = params.map() {NSURLQueryItem.init(name: $0, value: $1)}
print(nsurl)
//Result:
//[<NSURLQueryItem 0x7f8252d29730> {name = lat, value = 40}, <NSURLQueryItem 0x7f8252d29700> {name = lon, value = 100}]
I hope this helps
To you can create one expression like this:
(1...100).map(String.init)
The class has to support it, the String has one initializer with the following signature:
public init(stringInterpolationSegment expr: Int)
With allow it to you use the String.init referred as Int -> String.
But in your case the NSURLQueryItem has not the desired initializer, so the more close you can do it is using map like in the conventional way and passing the parameters to the init of the class NSURLQueryItem like in this way:
let listMapped = params.map { NSURLQueryItem(name: $0.0, value: $0.1 as? String) }
I hope this help you.
I looked at What's the cleanest way of applying map() to a dictionary in Swift? and came up with these two answers:
var params = ["lat": "40", "lon":"100"]
var p:[NSURLQueryItem] = []
var result1 = params.map { (key, value) in p.append(NSURLQueryItem(name:key, value:value)) } // version 1
var result2 = params.reduce([NSURLQueryItem]()) { $0 + [NSURLQueryItem(name:$1.0, value:$1.1)] } // version 2
In version 1, the parameter passed by map() is a (String, String) tuple. In version 2, the parameters passed by reduce() are [NSURLQueryItem] and a (String, String) tuple
Firstly, the the block or closure you're providing to the map function isn't quite right. Blocks are basically nameless functions, they take some number of parameters and return some type. If we were to be verbose, a solution would look something like this:
params.map { (a: (String, String)) -> NSURLQueryItem in
return NSURLQueryItem(name: a.0, value: a.1)
}
However we can simplify this bit of code. The dictionary is [String : String] so it can be inferred that the map function will take a (String, String) as a parameter, so we don't need to write that explicitly.
Swift also allows $n to refer to the nth element of a tuple parameter. Since we only have 1 parameter, $0 will refer to the first element of the first parameter.
The return type of the block can also be inferred, since we're creating a NSURLQueryItem, meaning we don't need to specify that either. This means we also don't need to write return in the block, we can just create the object.
Lastly, you should not call NSURLQueryItem.init() to create a new NSURLQueryItem, you should instead just say NSURLQueryItem().
Here's the minimal solution:
params.map { NSURLQueryItem(name: $0, value: $1) }