No exact matches in call to subscript , If I use forEach, does not work - swift

Please check the attached code below.
Why Pattern raise Error? , "No exact matches in call to subscript"
What is the difference between A and B?
This is the text import from the console.
3
Everest 8849
K2 8611
Kangchenjunga 8586
This is code
struct Mountain {
let name: String
let height: Int
}
func highestmountain() {
var mtList = [Mountain]()
let N = Int(readLine()!)!
(0..<N)
.forEach { _ in
/* Pattern A */
readLine()!
.split(separator: " ")
.forEach {
mtList.append(Mountain(name: "\($0[0])", height: Int("\($0[1])")!)) // Error: No exact matches in call to subscript
}
/* Pattern B */
let reads = readLine()!
.split(separator: " ")
mtList.append(Mountain(name: "\(reads[0])", height: Int("\(reads[1])")!)) // works!
}
}

In Pattern A, you're using split(" ") which creates an array of Strings (or, more specifically, String.SubSequence), and then you call forEach on that array.
forEach calls the attached closure for each item in the array. So, with your second input line, for example, on the first forEach, $0 will be "Everest" and on the second call, it'll be 8849. However, in your code, you're attempting to get $0[0], but $0 is not an array -- it's a single String.SubSequence. Thus the error about the subscript.
Your second approach (Pattern B) works because reads is an Array of String.SubSequence, so using a subscript (ie the [0]) works.
Unrelated to your question, but it's worth noting that using subscripts like [1] will fail and crash the app if you haven't first checked to make sure that the array has enough items in it. Force unwrapping with a ! (like you do with Int(...)!) can also cause crashes.

Related

swift 5.1 evaluate a closure in the wrong way

I'm having an issue with evaluation of one line of code
if i break it down to two lines, it's working , but in one line of code, it's just evaluate in a 'new' to a 'wrong' way.
my main reason for asking this question, is not to solve it, I know I can use parenthesis to solve it, and break it to Two line, but don't want to solve it, I just want to know why its evaluated like this , and if there's a solution for this : some setting to patch , in Order THAT it will work in ONE LINE OF CODE :
Heres the code that working in Two lines
Heres the code that trying to do the same thing, but rise an error as you can see:
full code of both working and not working :
class ClosuresStack {
var dic = Dictionary<String,(()->String)->String >()
subscript(_ str:String)-> (()->String)->String {
get {
return dic[str]!
}
set {
dic[str] = newValue
}
}
}
func createClosuresStak() -> ClosuresStack {
let cs = ClosuresStack()
func takesAClosureReturnA_string(_ closure:()->String) ->String {
return closure() + " Two"
}
cs["C"] = takesAClosureReturnA_string
return cs
}
let c = createClosuresStak()["C"]
let str = c{"One"}
print(str) // print: One Two
let c = createClosuresStak()["C"]{"One"} // error -->
now, I want to somehow understand how to change it that it will work in ONE LINE OF CODE : meaning that the evaluation of 'createClosuresStak()["C"]{"One"}' will create a closure after ["C"] , and then from that point writing the {"One"}
will make it a full evaluate of the line :
let c = createClosuresStak()["C"]{"One"}
making 'c' a String
if that's not possible, I need to know it Too , tnx :)
UPDATE
tnx for the comments , its help me understand the problem more clearly :
1) im understanding that the createClosuresStak()["C"]{"One"}
acutely trying to add the string 'One' as another parameter to the sub script , and there for the error from the compiler was that is cannot subscript (String,()->String} , 'C' as the string inside the [] , and the other parameter {"One"} -> BUT , isn't that some kind of a bug?, been that i'm using [] ,Cleary the compiler need to 'understand' that I want to subscript a String, also by power of inferring that swift has,
2) now I'm still trying to get that syntax to work as it is so I try to change some things, in order to get it to work :
so I created a function that take a string, and return a dictionary of type : Dictionary<String,()->String>, and then trying so subscript it
and the compiler don't rise an error that way :
func closuresDictionary(_ s:String) -> Dictionary<String,()->String> {
var dic = Dictionary<String,()->String>()
func foo()->String {
return s + " Two"
}
dic["C"] = foo
return dic
}
let c = closuresDictionary("One")["C"]{ "SomeString" }
c is now a closure of type ()->String which does noting with string that I put inside, so the syntax works, but the outcome is not doing anything.
when im changing the return type of the dictionary to a different closure : (String)->String instead of ()->String , im getting the same old error, that I'm trying to subscript a (String,(String)->String)
and I need a function that will take the string inside the {} , and create something from it meaning that I need to subscript to return a closure of (String)->String
its seems like there's no way to do that
im adding two more pictures of my last trying in order to get this line of code in current syntax to work
the wanted syntax working but the outcome is not an outcome not doing any thing with the string inside the {}:
same error, by changing the function to (String)->String
Your example:
let c = createClosuresStak()["C"]{"One"}
is using trailing closure syntax.
Trailing closure syntax works by including the trailing closure as an additional parameter to a function call. Subscripting an array is really a function call under the hood (to a function called subscript), and Swift is trying to pass that closure as a second parameter to the subscripting call, which is what the error is explaining:
Cannot subscript a value of type 'ClosuresStack' with an argument of type '(String, () -> String)'.
In other words, you can't pass both "C" and the closure {"One"} to the subscripting function.
There are at least 3 ways to fix this and still put it on one line:
Option 1: Use an explicit call to pass the closure instead of using trailing closure syntax
Wrap the closure in () to make the call explicit:
let c1 = createClosuresStak()["C"]({"One"})
print(c1)
Option 2: Wrap the createClosureStak()["C"] in parentheses
That lets Swift know the subscripting only gets "C" as a parameter and allows trailing closure syntax to work as expected:
let c2 = (createClosuresStak()["C"]){"One"}
print(c2)
Option 3: Add .self to the result before the trailing closure syntax:
That again finishes the subscripting call and avoids the confusion.
let c3 = createClosuresStak()["C"].self {"One"}
print(c3)
Personally, I would choose option one, because trailing closure syntax is unnecessary syntactic sugar that clearly is not working here.

Binary operator '==' cannot be applied to operands of type '[String]' and 'String

I am new to coding and I needed some help with answering this challenge.
Instructions:
// Given the two arrays below, write a function that takes a String as an input parameter and returns a Boolean value. The function should return true if the String input is in either array and it should return false if the String input is in neither array.
//
// Examples:
// Call your function and pass in the String "cat" as the input. Your function should return true
// Call your function and pass in the String "cow" as the input. Your function should return false
let array1 = ["dog", "cat", "bird", "pig"]
let array2 = ["turtle", "snake", "lizard", "shark"]
// Write your function below:
Here is where I have written my function:
func test(Animal:String){
let list = array1 + array2
for animal in list {
if list == animal {
print("true")
} else {
print("false")
}
}
}
test(Animal: "dog")
The error I am getting is:
Binary operator '==' cannot be applied to operands of type '[String]' and 'String under the if statement.
Help if you can and I apologize in advance if this is not formatted correctly.
Thanks!
Just a tip, in the future you should try adding a more descriptive question title, that is super vague and could mean anything.
Anyways your issue is with the line:
if list == animal {
The error you got is quite specific and tells you exactly what's going wrong. [String] is an array of strings while String is just a single item. So when you write for animal in list your taking one animal in that list at a time.
Comparing an animal to a list is not allowed. They are not the same type ([String] and String are different types, as the compiler error told you). You can only compare variables of the same type. So in your case a list (String array aka [String]) to a another list, or an animal (String) to an animal.
Now what you want to do is see if a string you got is in either array. There's actually a built in method in arrays that lets you do exactly this: .contains
func test(animal:String){
let list = array1 + array2
if list.contains(animal) {
print("true")
} else {
print("false")
}
}
or if you want to be extra concise with some good code you can try
func test(animal:String){
let list = array1 + array2
print(list.contains(animal)) // will be false if it doesn't so prints false, true if it does so prints true.
}
And another side note.. You want to be very careful doing an if else inside of a for loop where both the if and the else return. You shouldn't be making decisions after looking at only ONE element.
Because typically you want to check EVERY value before printing false, but when you have an if else, you'll return/print after checking ONLY the first value.
if you wanted to do it your way (no built in methods, iterating through the array) you would want to do something like this:
func test(animal:String){
let list = array1 + array2
for candidate in list {
if animal == candidate { // there's a match! we can return true
print("true ")
return // return says to exit the function since we no longer need to keep going this is appropriate
}
} // for loop ends
// we only return false once the ENTIRE for loop has run, because we know if it did exist, we would never get to this point in the code
// this part only runs once EVERY item has been checked and none were a match
print("false")
}
And finally as bilal says, never start variable names with capital letters. Typically capital letters mean its a type (like String). You'll notice I renamed Animal -> animal for you in my examples

Argument labels do not match any availble overloads

I'm trying to create an anagram tester, and I'm pretty sure the code I have should work, but I'm getting an error 'Argument labels '(_:)' do not match any available overloads' I've looked at the other posts regarding the same error, but I'm still not sure what this means or how to fix it.
var anagram1 : String!
var anagram2 : String!
var failure : Bool = false
var counter : Int = 0
print("Please enter first word: ")
anagram1 = readLine()
print("Please enter Second word: ")
anagram2 = readLine()
if anagram1.count == anagram2.count {
for i in anagram1.characters{
if (!failure){
failure = true
for y in anagram2.characters {
counter += 1
if i == y {
failure = false
anagram2.remove(at: String.Index(counter)) // error here
}
}
}
else {
print("these words are not anagrams")
break;
}
}
if (!failure) {
print("these words ARE anagrams")
}
}
else{
print ("these words aren't even the same length you fucking nonce")
}
To answer your first question: the error message Argument labels '(_:)' do not match any available overloads means that you've given a function parameter names or types that don't match anything Swift knows about.
The compiler is also trying to tell you what parameters to look at. '(_:)' says that you're calling a function with an unlabeled parameter. (That means a value without any parameter name. A common example of a function that would look like this is print("something"). In Swift documentation, this would look like print(_:).
Finally, overloads are ways to call a function with different information. Again using the print function as an example, you can call it multiple ways. A couple of the most common overloads would be:
// print something, followed by a newline character
print("something")
// print something, but stay on the same line
// (end with an empty string instead of the default newline character)
print("something", terminator: "")
Documented, these might look like print(_:) and print(_:, terminator:).
Note: these are broken down for explanation. The actual Swift documentation shows func print(_: Any..., separator: String, terminator: String) which covers a number of different overloads!
Looking at the line where the error occurs, you see a function call and an initializer (which is essentially a function). Documented, the way you've entered the parameters, the functions would look like: remove(at:) and String.Index(_:).
String.Index(_:) matches the parameters of the error message, so that's where your error is. There is no overload of the String.Index initializer that takes an unnamed parameter.
To fix this error, you need to find the correct way to create a String.Index parameter for the remove(at:) function. One way might be to try something like this:
for y in anagram2.characters.enumerated() {
// `y` now represents a `tuple`: (offset: Int, element: Character)
// so, you don't need `counter` anymore; use `offset` instead
if i == y.element { //`i` is a Character, so it can compare to `element`
...
let yIndex: String.Index = anagram2.index(anagram2.startIndex, offsetBy: y.offset)
anagram2.remove(at: yIndex)
...
}
}
However, there are other issues with your code that will cause further errors.
For one, you're looping through a string (anagram2) and trying to change it at the same time - not a good thing to do.
Good luck to you in solving the anagram problem!
Thanks for the help Leo but I found a way of doing it :)
if anagram1.count == anagram2.count {
for i in anagram1.characters{
if (!failure){
counter = -1
failure = true
for y in anagram2.characters {
counter += 1
if i == y {
failure = false
if counter < anagram2.count {
anagram2.remove(at: (anagram2.index(anagram2.startIndex, offsetBy: counter)))
break;
}
}
}
}

Swift: Can not use array filter in if let statement condition

Suppose I have an array of user's names
let users = ["Hello", "1212", "12", "Bob", "Rob"]
I want to get the first user whose name length is 2, so I filtered the array and got the first user
if let selected = users.filter{$0.characters.count == 2}.first {
print(selected)
}
This code is throwing a compilation error under swift 2.2
Consecutive statements on a line must be separated by ';'
However, this is working fine though
let selected = users.filter{$0.characters.count == 2}.first
if let selected = selected {
print(selected)
}
Can anyone explain why do I need to store filter result in a separate variable first? Any help would be really appreciated.
You can make this work by putting parentheses around the closure that you're passing to filter:
if let selected = users.filter({$0.characters.count == 2}).first {
print(selected)
}
That is the right way to do it. The trailing closure syntax doesn't work very well sometimes on lines with extra elements. You could also put parentheses around the whole statement:
if let selected = (users.filter {$0.characters.count == 2}.first) {
print(selected)
}
Swift is just having trouble parsing your statement. The parentheses give it help in how to parse the line. You should prefer the first way since the closure is indeed a parameter of filter, so enclosing it in parentheses makes it clear to Swift that you are passing it to filter.

How to handle initial nil value for reduce functions

I would like to learn and use more functional programming in Swift. So, I've been trying various things in playground. I don't understand Reduce, though. The basic textbook examples work, but I can't get my head around this problem.
I have an array of strings called "toDoItems". I would like to get the longest string in this array. What is the best practice for handling the initial nil value in such cases? I think this probably happens often. I thought of writing a custom function and use it.
func optionalMax(maxSofar: Int?, newElement: Int) -> Int {
if let definiteMaxSofar = maxSofar {
return max(definiteMaxSofar, newElement)
}
return newElement
}
// Just testing - nums is an array of Ints. Works.
var maxValueOfInts = nums.reduce(0) { optionalMax($0, $1) }
// ERROR: cannot invoke 'reduce' with an argument list of type ‘(nil, (_,_)->_)'
var longestOfStrings = toDoItems.reduce(nil) { optionalMax(count($0), count($1)) }
It might just be that Swift does not automatically infer the type of your initial value. Try making it clear by explicitly declaring it:
var longestOfStrings = toDoItems.reduce(nil as Int?) { optionalMax($0, count($1)) }
By the way notice that I do not count on $0 (your accumulator) since it is not a String but an optional Int Int?
Generally to avoid confusion reading the code later, I explicitly label the accumulator as a and the element coming in from the serie as x:
var longestOfStrings = toDoItems.reduce(nil as Int?) { a, x in optionalMax(a, count(x)) }
This way should be clearer than $0 and $1 in code when the accumulator or the single element are used.
Hope this helps
Initialise it with an empty string "" rather than nil. Or you could even initialise it with the first element of the array, but an empty string seems better.
Second go at this after writing some wrong code, this will return the longest string if you are happy with an empty string being returned for an empty array:
toDoItems.reduce("") { count($0) > count($1) ? $0 : $1 }
Or if you want nil, use
toDoItems.reduce(nil as String?) { count($0!) > count($1) ? $0 : $1 }
The problem is that the compiler cannot infer the types you are using for your seed and accumulator closure if you seed with nil, and you also need to get the optional type correct when using the optional string as $0.