I have code:
let number: String = "111 15 111"
let result = number.components(separatedBy: " ").map {Int($0)!}.reduce(0, {$0 + $1})
First it takes given string and split into array of numbers. Next each number is converted to an integer and at the end all numbers are added to each other. It works fine but code is a little bit long. So I got the idea to get ride of map function and convert String to Int while using reduce, like this:
let result = number.components(separatedBy: " ").reduce(0, {Int($0)! + Int($1)!})
and the output is:
error: cannot invoke 'reduce' with an argument list of type '(Int, (String, String) -> Int)'
Thus my question is: Why I cannot convert String to Integer while using reduce()?
reduce second parameter is a closure with $0 is the result and $1 is the string. And instead of force unwrapping optional default value will be better.
let number: String = "111 15 111"
let result = number.components(separatedBy: " ").reduce(0, {$0 + (Int($1) ?? 0) })
Another alternative is with flatMap and reduce with + operator.
let result = number.components(separatedBy: " ").flatMap(Int.init).reduce(0, +)
Your mistake is first argument in the closure. If you look at reduce declaration, first closure argument is Result type, which is Int in your case:
public func reduce<Result>(_ initialResult: Result,
_ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
So the right code will be:
let result = number.components(separatedBy: " ").reduce(0, { $0 + Int($1)! })
Related
As the title suggests, I'm trying to sort an existing Dictionary of type [Character : Int] based on the value count, with most repeated characters appearing first. The following code returns the error error: cannot convert return expression of type '[(key: Character, value: Int)]' to return type '[Character : Int]. Now, I get that my function is returning an array of Tuples but I can't understand how...
Here's the code:
let str = "the brown fox jumps over the lazy dog"
func characterCount(str: String) -> [Character : Int] {
var results: [Character : Int] = [:]
for char in str {
results[char] = 0
let charCount = str.filter { char == $0 }
results[char] = charCount.count
}
let sortedResults = results.sorted { $0.value < $1.value }
return sortedResults
}
print(characterCount(str: str))
As Sweeper notes, dictionaries are inherently unordered, so you will need to transform the dictionary into something sortable. The simplest option here is an array of tuples
let sorted = str
.reduce(into: [Character: Int]()){ $0[$1] = $0[($1), default: 0] + 1}
.map{($0.key, $0.value)}
.sorted{$0.1 > $1.1}
This answer uses reduce(into:) to create the dictionary from the string, but you could use a for loop if you prefer. It then maps the dictionary into an array of tuples of (Character, Int) where the Character is the dictionary key (i.e. the string character) and the Int is it's value (i.e. the character's count), and sorts that based on the value of the count.
I have an input (String): "1 * 2 + 34 - 5" and I want to split it into array and then convert "convertible" elements into integers. My code looks like this:
let arr: [Any] = readLine()!.split(separator: " ").map {Int($0) != nil ? Int($0) : $0}
Splitting is not a problem but I don't know why mapping doesn't work as it should. I get error:
error: cannot invoke initializer for type 'Int' with an argument list of type '((AnySequence<String.Element>))'
note: overloads for 'Int' exist with these partially matching parameter lists: (Float), (Double), (Float80), (Int64), (Word), (NSNumber), (CGFloat)
I try to do the same in another way, with initializing new array:
let arr = readLine()!.split(separator: " ")
let newArray: [Any] = arr.map {Int($0) != nil ? Int($0) : $0}
but it also throws me an error:
error: 'map' produces '[T]', not the expected contextual result type '[Any]'
It is suprise for me that when I'm trying to do the same with for loop it works perfectly:
let arr = readLine()!.split(separator: " ")
var newArray = [Any]()
for x in arr
{
if Int(x) != nil {newArray.append(Int(x)!)}
else {newArray.append(x)}
}
print(newArray)
output: [1, "*", 2, "+", 34, "-", 5]
Can someone explain to me what is going on here? I mean if all 3 codes do the same thing so why only "for loop" works fine?
You'll need to specify that the return type of your map block is Any rather than the type which is being inferred by the compiler (Int), e.g.
let fullString = "1 * 2 + 34 - 5"
let elements = fullString
.components(separatedBy: " ")
.map { Int($0) ?? $0 as Any}
I am learning Swift higher order functions associated with Collections. I have following query with reduce
enum Coin : Int {
case Penny = 1
case Nickel = 5
case Dime = 10
case Quarter = 25
}
let coinArray: [Coin] = [.Dime, .Quarter, .Penny, .Penny, .Nickel, .Nickel]
coinArray.reduce(0,{ (x:Coin, y:Coin) -> Int in
return x.rawValue + y.rawValue
})
I am getting following error:
Declared closure result Int is incompatible with contextual type _
Let's see how reduce is declared:
public func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
See the type of nextPartialResult? It is (Result, Element) -> Result. What is the type of Result, in your case? It is Int, because you want to reduce the whole thing to an integer.
Therefore, passing a (Coin, Coin) -> Int does not really work here, does it?
You should pass in a (Int, Coin) -> Int instead.
coinArray.reduce(0,{ (x:Int, y:Coin) -> Int in
return x + y.rawValue
})
Or simply:
coinArray.reduce(0) { $0 + $1.rawValue }
Once you apply reduce onto the coinArray you get the following signature:
Ask yourself what is the type of the generic Result? Is it of type coin or of type Int? What is the type of nextPartialResult? Is it of type coin or of type Int?
The answer is: Result is an Int and and nextPartialResult is a closure 'that takes one parameter of type result which here is Int and another parameter of type coin and eventually returns an Int'
So the correct way of writing it is:
coinArray.reduce(0,{ (x, y) -> Int in
return x + y.rawValue
})
Or in a more meaningful sense you could have wrote:
coinArray.reduce(0,{ (currentResult, coin) -> Int in
return currentResult + coin.rawValue
})
also coinArray isn't a good name. Just write coins. It being plural makes is more readable than coinArray / arrayOfCoins!
I'm trying to understand some of the short hand syntax used by the map function.
The following is the setup
let array = [1, 2, 3]
// these make sense
let arr1 = array.map({String($0)})
let arr2 = array.map{String($0)}
let arr3 = array.map({ number in
return String(number)
})
let arr4 = array.map({ (number) -> String in
String(number)
})
Here is where the confusion lays. In swift I can forgo the curly braces for map, but this seems like something that can't be done, for my own functions where I have a trailing closure. Some magical inference that's being made perhaps? Also why is the String initialized in this way?
// this doesn't make sense. Foregoing the curly braces? I can't do that!!!
let arr5 = array.map(String.init)
let arr6 = array.map(String()) // Compile Error: Cannot convert value of type 'String' to expected argument type '#noescape (Int) throws -> _'
This is me trying to use similar syntax as map
func crap(block:(Int)-> String) {
print("Int to string block" + block(1));
}
// works obviously
crap{ "\($0) some garbage" }
// compile error : Anonymous closure argument not contained in a closure
crap( "\($0) some garbage" )
Distinguish parentheses () from curly braces {}.
In a sense, only the parentheses version is "real", because, after all, that is what a function call requires. In the parentheses when you call map, you put a function. It may be a function reference (i.e. the name of a function):
let arr = [1,2,3]
func double(i:Int) -> Int {return i*2}
let arr2 = arr.map(double)
Or it can be an anonymous function, meaning a function body in curly braces:
let arr = [1,2,3]
let arr2 = arr.map({$0*2})
But in that case, and that case only, you can (as a shortcut) use the "trailing closure" syntax instead:
let arr = [1,2,3]
let arr2 = arr.map(){$0*2}
But since map takes no other parameters, you can then also omit the parentheses — the only situation in Swift where you can call a function without parentheses:
let arr = [1,2,3]
let arr2 = arr.map{$0*2}
I am trying to join the elements of a String array via the reduce function. A tried for a bit now, but I can't get what the problem exactly is. This is what I believe should do the trick. I have tried other alternatives too, but given the huge amount I will wait for some input:
var genres = ["towel", "42"]
var jointGenres : String = genres.reduce(0, combine: { $0 + "," + $1 })
Error:
..:14:44: Cannot invoke '+' with an argument list of type
'(IntegerLiteralConvertible, combine: (($T6, ($T6, $T7) -> ($T6, $T7)
-> $T5) -> ($T6, ($T6, $T7) -> $T5) -> $T5, (($T6, $T7) -> ($T6, $T7) -> $T5, $T7) -> (($T6, $T7) -> $T5, $T7) -> $T5) -> (($T6, ($T6, $T7) -> $T5) -> $T5, (($T6, $T7) -> $T5, $T7) -> $T5) -> $T5)'
From my understanding, $0 should be inferred as a String and $1, by combination with $0, should result as a String too. I don't know what's the deal with the type system here. Any idea?
Your reduce closure should probably look like this:
var jointGenres : String = genres.reduce("", combine: { $0 == "" ? $1 : $0 + "," + $1 })
This has the "" instead of 0 like you had, and makes sure that there is no extra comma in the beginning of the return value.
The original code did not work because the return type that is represented as U in documentation was originally 0 in your answer, while you are trying to add a String to it. In your case, you really want both U and T to represent Strings instead of Ints.
reduce is not a straightforward solution here since you need special handling for the first element. String's join method is better for this purpose:
let strings = ["a", "b", "c"]
let joinedString = ",".join(strings)
If you know the array is not empty there is another possible solution with reduce that also avoids conditionals:
let joinedStrings = strings[1..<strings.count].reduce(strings[0]) { $0 + "," + $1 }
Cocoa already has a function to do this. It is marred by needing a typecast to NSArray.
var genres = ["towel", "42"]
var joinGenres = (genres as NSArray).componentsJoinedByString(",")
To my surprise, this function also can be applied to arrays of types other than String:
let ints = [1,5,9,15,29]
let listOfInts = (ints as NSArray).componentsJoinedByString(",")
If using Swift 4:
var jointGenres:String = genres.joined(separator: ",")
The problem is your first argument to reduce. This is an accumulator, it's an integer literal, and it's what's passed as $0 on the first run of the block. You're asking the reduce function to add a string to this.
Instead of 0 as the accumulator argument, you should be passing "", an empty string.
This works:
var genres = ["towel", "42"]
var jointGenres : String = genres.reduce("", combine: { $0 + "," + $1 })