for lops and func not printing the code Missing argument for parameter 'in' in call - swift

I'm trying to print out the names that comes after the search engine clears them so for example I wrote in the searchValue "Mohamed" expecting it to print all Mohameds in the usersSeenStory but it's giving me an error saying Missing argument for parameter 'in' in call
var usersSeenStory = ("ameerahmed_", "_mohamedalaaa", "afapps", "mohamed_khaled"); // Who seen the story are here.
var searchValue = "mohamed"; // This value is for example. It will be inserted in the search input.
func searchForUser(in arr:[String], for str: String) -> [String] {
for results in searchValue {
print(results)
}
}
searchForUser()

It seems you want to get only certain Users. To do this, you need to change your code to this:
var usersSeenStory = ["ameerahmed_", "_mohamedalaaa", "afapps", "mohamed_khaled"]
var searchValue = "mohamed";
func searchForUser(in arr:[String], for str: String) -> [String] {
var matchedUsers = [String]()
for user in arr {
if let _ = user.range(of: str) {
matchedUsers.append(user)
}
}
return matchedUsers
}
searchForUser(in: usersSeenStory, for: searchValue)
But there is also a shorter version to do this:
let filtered = usersSeenStory.filter { $0.range(of: searchValue) != nil}

In order to print all the Mohameds in your usersSeenStory array you just need to add a condition, like that:
var usersSeenStory = ("ameerahmed_", "_mohamedalaaa", "afapps", "mohamed_khaled"); // Who seen the story are here.
var searchValue = "mohamed"; // This value is for example. It will be inserted in the search input.
func searchForUser(in arr:[String], for str: String) {
for results in searchValue {
if results.contains(str) {
print(results)
}
}
}
searchForUser()
or
func searchForUser(in arr:[String], for str: String) -> [String] {
let results = arr.filter{ $0.contains(str) }
print(results)
return results
}

Related

How to cut last characters of string and save it as variable in swift?

I have a string of test#me
now I want to create a code that let me cut where the # is and save it as a variable, so the expected result is string1 = test string2 = #me
a code will be something like this
func test(string: String) {
var mString = string
var cuttedString = mString.cutFrom("#")
print(mString)
print(cuttedString)
}
test(string: "test#me")
result:
test
#me
Here is an extension of String which performs the function you desire. It takes a String and mutates the calling String by removing everything from that part on, and it returns the part that was removed.
import Foundation
extension String {
mutating func cut(from string: String) -> String {
if let range = self.range(of: string) {
let cutPart = String(self[range.lowerBound...])
self.removeSubrange(range.lowerBound...)
return cutPart
}
else {
return ""
}
}
}
func test(string: String) {
var mString = string
let cutString = mString.cut(from: "#")
print(mString)
print(cutString)
}
test(string: "test#me")
test
#me
A Generic Implementation
Here is a generic implementation suggested by #LeoDabus in the comments:
extension StringProtocol where Self: RangeReplaceableCollection {
#discardableResult mutating func removeSubrange<S: StringProtocol>(from string: S) -> SubSequence {
guard let range = range(of: string) else { return "" }
defer { removeSubrange(range.lowerBound...) }
return self[range.lowerBound...]
}
}
It nicely demonstrates:
Extending a protocol instead of String to allow the function to be used with other types such as String.Subsequence.
The use of #discardableResult which allows the function to be called to shorten the String without using the substring that is returned.
Using a guard let statement to unwrap the optional return from range(of:) and provide an early exit if the range is nil.
The use of defer to delay the removal of the substring until after the substring has been returned which avoids the use a local variable.
Use suffix(from:) in combination with firstIndex(of:)
let string = "test#me"
let last = string.suffix(from: string.firstIndex(of: "#") ?? string.startIndex)
Note that this returns the full string if "#" is not found, you could instead return an empty string by replacing string.startIndex with string.endIndex
To split the string in two parts, ie "test" and "#me"
var first: String = ""
var last: String = ""
if let index = string.firstIndex(of: "#") {
last = String(string.suffix(from: index))
first = String(string.prefix(upTo: index))
}
print(first, last)
You can use String subscript for this:
func test(string: String) {
guard let atIndex = string.firstIndex(of: "#") else { return }
let mString = string[string.startIndex...string.index(before: atIndex)]
let cuttedString = string[atIndex..<string.endIndex]
print(mString)
print(cuttedString)
}
func test(string: String) {
let mString = string.prefix { $0 != "#" }
let cuttedString = string.suffix(from: string.firstIndex(of: "#") ?? string.startIndex)
print(mString)
print(cuttedString)
}
test(string: "test#me")
// prints test
// #me
But remember that mString and cuttedString are not String but Substring, so take care of correct usage.
There are various ways to do this.
You could use components(separatedBy:) to break the string into pieces, and then add an # back to the last one:
extension String {
func cutFrom(_ divider: Character) -> String {
let array = components(separatedBy: String(divider))
//For an Optional, `map()` returns nil if the optional is empty,
//Or the result of applying the closure to the unwrapped contents if not
return array.last.map {String(divider) + $0} ?? ""
}
}
Alternately, you could use firstIndex to find the index of the first divider character in the string, and then return a substring from that index to the end:
extension String {
func cutFrom(_ divider: Character) -> String {
//For an Optional, `map()` returns nil if the optional is empty,
//Or the result of applying the closure to the unwrapped contents if not
return firstIndex(of: divider).map { String(self[$0..<endIndex])} ?? ""
}
}
It might be cleaner to have both versions of the function return Optionals, and return NIL if the divider character can't be found. You also might want to adjust the logic to deal with strings where the divider character occurs more than once.

How to filter objects of certain type from [AnyObject] array

Is it possible to filter an array of [AnyObject] to yield all elements of a given type, and none other?
I can do it if the type is known at compile time:
class MyClass1: CustomStringConvertible {
var value: Int
var description: String {
return "MyClass1: \(value)"
}
init(_ value: Int) {
self.value = value
}
}
class MyClass2: CustomStringConvertible {
var value: Int
var description: String {
return "MyClass1: \(value)"
}
init(_ value: Int) {
self.value = value
}
}
class MySubClass1: MyClass1 {
override var description: String {
return "MySubClass1: \(value)"
}
}
let a1 = MySubClass1(1)
let a2 = MySubClass1(2)
let b1 = MyClass1(3)
let b2 = MyClass2(4)
let array: [AnyObject] = [a1, b1, a2, b2]
func getClass1ObjectsFromArray(_ array: [AnyObject]) -> [MyClass1] {
return array.compactMap( { $0 as? MyClass1 })
}
func getSubClass1ObjectsFromArray(_ array: [AnyObject]) -> [MySubClass1] {
return array.compactMap( { $0 as? MySubClass1 })
}
print(getClass1ObjectsFromArray(array))
print(getSubClass1ObjectsFromArray(array))
Prints:
[MySubClass1: 1, MyClass1: 3, MySubClass1: 2]
[MySubClass1: 1, MySubClass1: 2]
For every type I want to filter on, I had to write a separate function. This looks ugly to me, and will not work when the type to be selected for is only known at run time.
Question:
Is there a generic way to write such a function? Preferably something like:
func getObjectsOfType(_ type: TypeExpression, fromArray array: [AnyObject])
-> [TypeExpression] {
...
}
Or any other way to achieve this?
Thanks for any help!
I think you could use something like this...
let filteredArray = array.compactMap { $0 as? RequiredType }
This will filter the array and return a typed array containing only the type you want.
Caveat
Having said that. In Swift you should be avoiding heterogeneous arrays where possible. Arrays should really only contain one type of item.
A bit of code testing...
Tested in Playground...
let array: [Any] = [1, "hello", 3, 3.1415, "world"]
let filteredArray = array.compactMap { $0 as? String }
filteredArray
Output:
filteredArray = ["hello", "world"]
👍🏻
Edit 1
You could also create a generic function something like this...
func filter<T>(array: [Any]) -> [T] {
return array.compactMap { $0 as? T }
}
let filteredArray: [String] = filter(array: array)
This will then filter based on the type of the output array that you want.
I'm not sure what you mean by only knowing the type you want at run time. Can you give a more concrete example of what you mean?
Edit 2
Another possibility is a generic function like this...
func filter<T>(array: [Any], byType typeObject: T) -> [T] {
return array.compactMap { $0 as? T }
}
let filteredArray = filter(array: array, byType: "some string")
This uses the type information of the second parameter to filter the array by that type of item.
Edit 3
If you don't like passing in an instance of the type then you can pass the type itself...
func filter<T>(array: [Any], byType typeObject: T.Type) -> [T] {
return array.compactMap { $0 as? T }
}
let filteredArray = filter(array: array, byType: String.self)
But I'm not sure what more you're getting from this than just filtering by string in the first place?

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 compare the String value of a [[String]] to a String?

I want to be able to compare values from the [String] level instead of the String level. Here's what I mean:
var connectedNames:[[[String]]] = [[[]]]
for var row: Int = 0; row < connectedNames[0].count; row++ {
if self.connectedNames[0][row] as! String == "asdf" {
}
}
But the cast here from [String] to String fails so I can't make this value comparison.
So the main problem is this: Is there anyway to compare the String value of a [[String]] to a String? In other words, the String value that I get from indexing connectedNames like so connectedNames[0][0] == "Some String"?
You can only compare [[String]] to String by using the subscript method of the Array to access the inner element. This would work:
func compare() -> Bool {
let arr: [[String]] = [["foo"]]
let str: String = "foo"
guard let innerArr = arr[0] else {
return false
}
guard let element = innerArr[0] else {
return false
}
return element == str
}

Swift: Avoid imperative For Loop

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]} ?? [])
}