Swift: Avoid imperative For Loop - swift

What I'm trying to accomplish in imperative:
var mapNames = [String]()
var mapLocation = [String]()
for valueMap in valueMaps {
if let name = valueMap.name {
mapNames.append(name)
}
if let location = valueMap.location {
mapLocation.append(location)
}
}
What's the best way using a high order function or perhaps an array method (array.filter etc.) to compact the code above and also avoid using the for loop
Here is what I have tried, but the compiler gives an error:
let getArrayOfNames = valueMaps.filter() {
if let name = ($0 as valueMaps).name as [String]! {
return name;
}
}
let getArrayOfLocations = valueMaps.filter() {
if let type = ($0 as valueMaps).location as [String]! {
return type;
}
}

You need both filter() and map() :
let mapNames = valueMaps.filter( {$0.name != nil }).map( { $0.name! })
let mapLocations = valueMaps.filter( {$0.location != nil }).map( { $0.location! })
The filter takes a predicate as an argument (which specifies which
elements should be included in the result), and the map takes
a transformation as an argument. You were trying to merge both
aspects into the filter, which is not possible.
Update: As of Swift 2(?) has a flatMap() method for sequences, which
can be used to obtain the result in a single step:
let mapNames = valueMaps.flatMap { $0.name }
The closure is applied to all array elements, and the return value is an
array with all non-nil unwrapped results.

The filter() function needs its closure to return a bool - not the value you want to store in an array. You could chain filter and map together to get what you want, then:
let getArrayOfNames = valueMaps
.filter { $0.name != nil }
.map{ $0.name! }
Or, to do it in one function, with reduce:
let getArrayOfNames = valueMaps
.reduce([String]()) {
accu, element in
if let name = element.name {
return accu + [name]
} else {
return accu
}
}
Actually, the reduce can be a little better:
let getArrayOfNames = valueMaps.reduce([String]()) {
(names, value) in names + (value.name.map{[$0]} ?? [])
}
let getArrayOfLocations = valueMaps.reduce([String]()) {
(locs, value) in locs + (value.location.map{[$0]} ?? [])
}

Related

Initialize KeyValuePairs from array, from dictionary

According to the doc, KeyValuePairs is lightweight collection of key-value pairs. Much like an ordered Dictionary. KeyValuePairs has similar initializer to init from literal. But unlike it's unordered counterpart it lacks inits from sequences of key-value tuples. Can we add more inits, to obtain KeyValuePairs from Sequences, Collections or Dictionary or at least from array of tuples?
For example, I need a sorted version of such a Dictionary
static var allRegionIsoCodesWithNames: Dictionary<String, String> {
get {
return Dictionary(uniqueKeysWithValues: Locale.isoRegionCodes.map({ ($0, String.emojiFlag(for: $0)! + LWUserDefaults.locale.localizedString(forRegionCode: $0)!) }).sorted(by: { $0.0 < $1.0 }))
}
}
/* A useless code since ordering is not applyed */
I need this one to work
static var allRegionIsoCodesWithNamesKeyVal: KeyValuePairs<String, String>? {
get {
return Array(Locale.isoRegionCodes.map({ ($0, String.emojiFlag(for: $0)! + LWUserDefaults.locale.localizedString(forRegionCode: $0)!) }).sorted(by: { $0.0 < $1.0 }))
}
}
/* Cannot convert return expression of type '[(String, String)]' to return type 'KeyValuePairs<String, String>?' */
In order to reproduce here is the String.emojiFlag (for reference):
extension String {
static func emojiFlag(for countryCode: String) -> String? {
func isLowercaseASCIIEnglishLetterScalar(_ scalar: Unicode.Scalar) -> Bool {
return scalar.value >= Unicode.Scalar("a").value && scalar.value <= Unicode.Scalar("z").value
}
func regionalIndicatorSymbol(for scalar: Unicode.Scalar) -> Unicode.Scalar {
precondition(isLowercaseASCIIEnglishLetterScalar(scalar))
return Unicode.Scalar(scalar.value + (0x1F1E6 - 0x61))!
}
let lowercasedCode = countryCode.lowercased()
guard lowercasedCode.unicodeScalars.allSatisfy(isLowercaseASCIIEnglishLetterScalar) else {return nil }
let indicatorSymbols = lowercasedCode.unicodeScalars.map({ regionalIndicatorSymbol(for: $0) })
return String(indicatorSymbols.map({ Character($0) }))
}
}

How do you assign and unwrap an optional array object in Swift?

I'm trying to invert a binary tree in Swift using the iterative approach. Essentially I just keep looping through each node, and putting it into a stack array. This array should be an array of optional nodes. Here is my code:
func invertTree(_ root: TreeNode?) -> TreeNode? {
if root != nil {
stack.append(root)
} else {
return root
}
var stack = [TreeNode?]()
while stack.count > 0 {
if stack.last == nil {
stack.removeLast()
} else {
var tempLeft : TreeNode? = stack.last!.left
stack.last!.left = stack.last!.right
stack.last!.right = tempLeft
if stack.last!.left != nil {
stack.append(stack.last!.left)
}
if stack.last.right != nil {
stack.append(stack.last!.right)
}
}
}
return root
}
I'm getting all sorts of optional errors. In particular, when I set:
var tempLeft : TreeNode? = stack.last!.left
I'm not sure why it says "value of optional type TreeNode? must be unwrapped to refer to member 'left' of unwrapped base type 'TreeNode' in solution.swift"
I don't understand why it's telling me to unwrap the optional, when I'm already force unwrapping it. I don't want to unwrap the .left node since I want to include nils in my stack for the iteration to work.
Any help would be greatly appreciated.
stack.last is a doubly optional TreeNode: TreeNode??.
You could use optional chaining like so:
if stack.last == nil {
stack.removeLast()
} else {
stack.last
var tempLeft : TreeNode? = stack.last!?.left
stack.last!?.left = stack.last!?.right
stack.last!?.right = tempLeft
if stack.last!?.left != nil {
stack.append(stack.last!?.left)
}
if stack.last??.right != nil {
stack.append(stack.last!?.right)
}
}
Or avoid the drama and use pattern matching:
if case let node?? = stack.last {
let tempLeft = node.left
node.left = node.right
node.right = tempLeft
...
Other than that, it seems that there are other areas of your code that you need to tend to. For example, using stack before its declaration.
Alternative implementation
Here is a solution that uses a stack:
class Solution {
func invertTree(_ root: TreeNode?) -> TreeNode? {
guard let r = root else {
return nil
}
var stack = [r]
while let node = stack.last {
let temp = node.left
node.left = node.right
node.right = temp
stack.removeLast()
if let left = node.left { stack.append(left) }
if let right = node.right { stack.append(right) }
}
return r
}
}

Why does this sorting algorithm fail?

I developed the below algorithm to sort the array of dictionary items.
guard var imageUrlString = anyImage.value as? [String:AnyObject] else { return }
var values = [AnyObject]()
var keys = [String]()
var done = false
var j = 1
while !done {
for i in imageUrlString {
print(i.key, " this is the key")
if "media\(j)" == i.key {
values.append(i.value)
keys.append(i.key)
print(i, " This is teh i for in if ")
if imageUrlString.count == j {
done = true
break;
}
j+=1
} else {
print("No,,.")
}
}
}
The problem is that sometimes, for example, every time the first media is an image, it will loop forever. How can I solve that so that the algorithm can effectively sort the under all conditions?
It looks like you're really creating two parallel arrays: keys and values.
I went with creating those two arrays, sorted. Here's an example
var imageUrlString = [String: AnyObject]()
imageUrlString["media3"] = "whatever 3 content" as AnyObject
imageUrlString["media7"] = "whatever 7 content" as AnyObject
imageUrlString["media1"] = "whatever 1 content" as AnyObject
let keys = Array(imageUrlString.keys).sorted()
var values = [AnyObject]()
keys.forEach {
values.append(imageUrlString[$0]!)
}
print(keys)
print(values)

How to remove duplicate characters from a string in Swift

ruby has the function string.squeeze, but I can't seem to find a swift equivalent.
For example I want to turn bookkeeper -> bokepr
Is my only option to create a set of the characters and then pull the characters from the set back to a string?
Is there a better way to do this?
Edit/update: Swift 4.2 or later
You can use a set to filter your duplicated characters:
let str = "bookkeeper"
var set = Set<Character>()
let squeezed = str.filter{ set.insert($0).inserted }
print(squeezed) // "bokepr"
Or as an extension on RangeReplaceableCollection which will also extend String and Substrings as well:
extension RangeReplaceableCollection where Element: Hashable {
var squeezed: Self {
var set = Set<Element>()
return filter{ set.insert($0).inserted }
}
}
let str = "bookkeeper"
print(str.squeezed) // "bokepr"
print(str[...].squeezed) // "bokepr"
I would use this piece of code from another answer of mine, which removes all duplicates of a sequence (keeping only the first occurrence of each), while maintaining order.
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var alreadyAdded = Set<Iterator.Element>()
return self.filter { alreadyAdded.insert($0).inserted }
}
}
I would then wrap it with some logic which turns a String into a sequence (by getting its characters), unqiue's it, and then restores that result back into a string:
extension String {
func uniqueCharacters() -> String {
return String(self.characters.unique())
}
}
print("bookkeeper".uniqueCharacters()) // => "bokepr"
Here is a solution I found online, however I don't think it is optimal.
func removeDuplicateLetters(_ s: String) -> String {
if s.characters.count == 0 {
return ""
}
let aNum = Int("a".unicodeScalars.filter{$0.isASCII}.map{$0.value}.first!)
let characters = Array(s.lowercased().characters)
var counts = [Int](repeatElement(0, count: 26))
var visited = [Bool](repeatElement(false, count: 26))
var stack = [Character]()
var i = 0
for character in characters {
if let num = asciiValueOfCharacter(character) {
counts[num - aNum] += 1
}
}
for character in characters {
if let num = asciiValueOfCharacter(character) {
i = num - aNum
counts[i] -= 1
if visited[i] {
continue
}
while !stack.isEmpty, let peekNum = asciiValueOfCharacter(stack.last!), num < peekNum && counts[peekNum - aNum] != 0 {
visited[peekNum - aNum] = false
stack.removeLast()
}
stack.append(character)
visited[i] = true
}
}
return String(stack)
}
func asciiValueOfCharacter(_ character: Character) -> Int? {
let value = String(character).unicodeScalars.filter{$0.isASCII}.first?.value ?? 0
return Int(value)
}
Here is one way to do this using reduce(),
let newChar = str.characters.reduce("") { partial, char in
guard let _ = partial.range(of: String(char)) else {
return partial.appending(String(char))
}
return partial
}
As suggested by Leo, here is a bit shorter version of the same approach,
let newChar = str.characters.reduce("") { $0.range(of: String($1)) == nil ? $0.appending(String($1)) : $0 }
Just Another solution
let str = "Bookeeper"
let newChar = str.reduce("" , {
if $0.contains($1) {
return "\($0)"
} else {
return "\($0)\($1)"
}
})
print(str.replacingOccurrences(of: " ", with: ""))
Use filter and contains to remove duplicate values
let str = "bookkeeper"
let result = str.filter{!result.contains($0)}
print(result) //bokepr

How to sort array according to number of occurrence of string?

How to sort array according to number of occurrence of string
Example :
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
and sorted array should be like this
["Me","Hello","That","as","the"]
Updated For Swift 3
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
var counts:[String:Int] = [:]
for item in array {
counts[item] = (counts[item] ?? 0) + 1
}
print(counts)
let result = counts.sorted { $0.value > $1.value }.map { $0.key }
print(result)
array.removeAll()
for string in result {
array.append(string)
}
print(array)
This is what I have been able to come up with:
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
// record the occurences of each item
var dict = [String: Int]()
for item in array {
if dict[item] == nil {
dict[item] = 1
} else {
dict[item]! += 1
}
}
// here I sort the dictionary by comparing the occurrences and map it so that the result contains only the key (the string)
let result = dict.sorted { $0.value > $1.value }.map { $0.key }
Try this -
It is tested and working as expected --
let arrayName = ["Hello","Me","That","Me","Hello","Me","as","the"]
var counts:[String:Int] = [:]
for item in arrayName {
counts[item] = (counts[item] ?? 0) + 1
}
let array = counts.keysSortedByValue(isOrderedBefore: >)
print(array) // Output - ["Me", "Hello", "the", "That", "as"]
Create Dictionary extension -
extension Dictionary {
func sortedKeys(isOrderedBefore:(Key,Key) -> Bool) -> [Key] {
return Array(self.keys).sorted(by: isOrderedBefore)
}
// Faster because of no lookups, may take more memory because of duplicating contents
func keysSortedByValue(isOrderedBefore:(Value, Value) -> Bool) -> [Key] {
return Array(self)
.sorted() {
let (_, lv) = $0
let (_, rv) = $1
return isOrderedBefore(lv, rv)
}
.map {
let (k, _) = $0
return k
}
}
}
It looks simple.
1. Take distinct from your array.
2. Make count according to distinct list.
3. Save results in collection - ie Dictionary.
4. Sort new collection.
Loop through the array and maintain a word count dictionary. Make sure the dictionary can be sorted based on values and finally obtain the set of keys and transform it back into an array.
This should work.
var array = ["Hello","Me","That","Me","Hello","Me","as","the"]
var tR : [String : Int] = [:]
let finalResult = array.reduce(tR) { result, item in
var tArr : [String: Int] = result
if let count = tArr[item] {
tArr[item] = count+1
} else {
tArr[item] = 1
}
return tArr
}
.sorted(by: { item1, item2 in
return item1.value > item2.value
}).map() { $0.key }
Please try this, hope it helps
var terms = ["Hello","Me","That","Me","Hello","Me","as","the"]
var termFrequencies = [String: Int]()
for t in terms {
if termFrequencies[t] == nil {
termFrequencies[t] = 1
} else {
termFrequencies[t] = termFrequencies[t]! + 1
}
}
for value in terms {
let index = termFrequencies[value] ?? 0
termFrequencies[value] = index + 1
}
let result = termFrequencies.sorted{$0.1 > $1.1}.map{$0.0}