'T' is not identical to 'Int' - swift

I'm trying some sample code in Swift and combined two principles: Array.map and Extension. Unfortunately, I get the error code "'T' is not identical to 'Int'" (line 3). So my question should probably be: how can I transform a T array into a Int array?
Thanks in advance for your answers!
Cheers.
extension Array {
func translateToDigitalNames()-> [String] {
var numbers : [Int] = self
let digitNames =
[0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"]
var strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
return strings
}
}
let x = [26, 158, 610]
x.translateToDigitalNames()

I would split the problem into 3 distinct subproblems.
The first is translating an integer into a string representation, implemented as a static method:
private static func translateToDigitalName(var number: Int) -> String {
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
The reasons for making it a separate function are: separation of concerns, reusability, readability.
Next, we can implement a static method that, given an array of integers, returns an array of their respective string representations:
private static func translateToDigitalNames(numbers: [Int])-> [String] {
return numbers.map { self.translateToDigitalName($0) }
}
Last, the actual array extension. The approach I'm using is to filter the array by excluding elements not castable to Int:
func translateToDigitalNames()-> [String] {
let numbers: [Int] = self.filter { $0 is Int }.map { $0 as Int }
return Array.translateToDigitalNames(numbers)
}
If you want the translation to fail if at least one array element is not an Int, just add an explicit check and return nil in that case:
func translateToDigitalNames()-> [String]? {
let numbers: [Int] = self.filter { $0 is Int }.map { $0 as Int }
if numbers.count != self.count {
return nil
}
return Array.translateToDigitalNames(numbers)
}
Note that the return type is now an optional.
Last thing, rather than creating the digitNames array at every function invocation, I've moved it as a private global and immutable variable
The full code is available on this gist
Side note: the translateToDigitalNames and translateToDigitalName can be moved outside of the array extension, as global and public functions - and that's actually what I'd recommend in case you need to reuse them in the future.

You can create the numbers array with a map function as well, like:
var numbers = map { $0 as Int }

It's not a good idea to write such kind of extension since it applies only to [Int] arrays. What should happen if you would do
["some", "strings"].translateToDigitalNames()
Instead use a function like this:
func translateToDigitalNames(numbers:[Int])-> [String] {
let digitNames =
[0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"]
var strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
return strings
}
let x = [26, 158, 610]
translateToDigitalNames(x)
So the compiler ensures you always supply a [Int]

Related

Swift 5 How can I hash two arrays

How can I make this code SWIFT accepting? I've got two arrays of type ANY one array's value should act as the key, the other one as the appropriate value:
let it_tt_ar = db.pair(keys: "int_test", values: "text_test");
func _pair<K : Hashable, V>(keys: [K], values: [V]) -> Dictionary<K,V> {
var result = Dictionary<K, V>();
for i in 0...(keys.count - 1) {
result[keys[i]] = values[i];
}
return result;
}
func pair (keys: String?, values: String?) -> Dictionary<Int32,Any> {
if let _keys = keys, let _values = values {
let result = _pair(keys: hashtable[_keys] as! [Int32], values: hashtable[_values]!);
return result;
} else {
return [:];
}
}
I can't get it working if the type of the key is unknown. I want to write it like this:
let it_tt_ar = db.pair<Int32,String>(keys: "int_test", values: "text_test");
or
let it_tt_ar = db.pair(keys: "int_test", values: "text_test", kt:(Int32.self,String.self));
... in the last case by catching kt: in the function
But there's seems no chance to win against SWIFT:
cannot specify generic functions
or
Int32 cannot fulfill the hashable protocol
It's terrible! You want to write application logic but 80% of the development time is wasted by got to have fulfill such rules!
It looks like you're trying to turn a pair of arrays into a dictionary, regardless of the type of the array (provided, of course, that the type of the key array element is hashable). Here is one way:
let k : [Int] = [1,2,3]
let v : [String] = ["one", "two", "three"]
func pair<Key, Value>(keyArray keys:[Key], valueArray values:[Value]) -> Dictionary<Key,Value> where Key:Hashable {
zip(keys,values).reduce(into: Dictionary<Key,Value>()) {
(dict, tuple) in dict[tuple.0] = tuple.1
}
}
let result = pair(keyArray: k, valueArray: v)
print(result) // [1: "one", 2: "two", 3: "three"], in some order
Found a solution that works for me:
var db = try DataBaseSqlite(dbfile: "test.db");
try db.select(sql: "int_test, real_test, text_test from stest");
var it = db.valueByKey(key: "int_test");
var rt = db.valueByKey(key: "real_test");
var tt = db.valueByKey(key: "text_test");
let it_tt_ar = db.pair(keys: "int_test", values: "text_test", kt: Int32.self);
let tt_it_ar = db.pair(keys: "text_test", values: "int_test", kt: String.self);
try db.close();
func _pair<K : Hashable, V>(keys: [K], values: [V]) -> Dictionary<K,V> {
var result = Dictionary<K, V>();
for i in 0...(keys.count - 1) {
result[keys[i]] = values[i];
}
return result;
}
func pair<T>(keys: String?, values: String?, kt: T.Type) -> Dictionary<T,Any> {
if let _keys = keys, let _values = values {
let result = _pair(keys: hashtable[_keys] as! [T], values: hashtable[_values]!);
return result;
} else {
return [:];
}
}
Due to lack of supporting a real hashtable in Swift (like c# does), my hashtable is just an Dictionary of <String,Array> which is automatically built up by the select method.
So from an application point of view I can write a more efficient and generic code to query sqlite databases.
dbValueByKey() returns a typed (requested) Array of the column values and pair() returns just a combination of two columns.

TwoSum Swift Solution

I just started learning coding with swift, and was trying TwoSum.
"Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1]."
I found some solutions from GitHub that I cannot understand.
code is from https://github.com/soapyigu/LeetCode-Swift/blob/master/Array/TwoSum.swift
class TwoSum {
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var dict = [Int: Int]()
for (i, num) in nums.enumerated() {
if let lastIndex = dict[target - num] {
return [lastIndex, i]
}
dict[num] = i
}
fatalError("No valid outputs")
}
}
Could someone be so kind to explain to codes. Thanks a lot.
The dict initialised in the method stores the numbers in the input as keys, and their indices as values. The program uses this to remember which number is where. The dict can tell you things like "the number 2 is at index 0".
For each number num at index i in the input array, we subtract num from the target to find the other number that we need, in order for them to add up to target.
Now we have the other number we need, we check to see if we have seen such a number before, by searching dict. This is what the if let lastIndex = dict[target - num] part is doing. If the dict knows what index the other number is at, we return that index, and i.
If we haven't seen that number before, we record i into the dictionary under the key num, hoping that in later iterations, we can find a number that when added to num, makes 9.
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
var arr:[Int] = []
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var toggle = false
for i in 0..<nums.count {
for j in i+1..<nums.count {
if toggle == false {
if(nums[i]+nums[j]==target){
toggle = true
arr.insert(i, at: 0)
arr.insert(j, at: 1)
break
}
}
}
}
return arr
}
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
In Sweeper's excellent answer, he explained what dict is used for: It lets you use a value from the array to find that value's index. It would be more obvious what the dictionary was used for if we called it indexes, and this code builds the same dictionary in a more explicit way:
var indexes = [Int: Int]()
for index in 0..<array.count {
let value = array[index]
indexes[value] = index
}
After that, you get a dictionary:
[2:0, 7:1, 11:2, 15:3]
You could write the function this way:
func twoSum(_ array: [Int], _ target: Int) -> [Int] {
var indexes = [Int: Int]()
for index in 0..<array.count {
let value = array[index]
indexes[value] = index
}
for index in 0..<array.count {
let value = array[index]
if let otherIndex = indexes[target - value],
index != otherIndex {
return [index, otherIndex]
}
}
fatalError("Unable to match values")
}
That is a much more long-winded (and less efficient) way of doing the same thing. It loops through the array twice instead of once, but the results should be the same.
func twoSum(array: [Int], target: Int) -> [Int] {
var dict = [Int:Int]()
for (index, number) in array.enumerated() {
let value = target - number
if let sum = dict[value] {
return [sum, index]
}
dict[number] = index
}
return [0,0]
}
/*
array=[1, 2, 3] -> target=4
enumerated() => [0,1], [1,2], [2,3]
(i, n)
v4 - 1 = 3
s[3:0]
s[3:0]
v4 - 2 = 2
s[2:0]
s[2:1]
v4 - 3 = 1
s[1:1]
s[1:2]
output [0,2]
*/
var numbers: [Int] = [1, 3, 6, 7, 7, 14, 12]
var target = 26
var result = [Int]()
for i in 0..<numbers.count {
for j in i+1..<numbers.count {
if numbers[i] + numbers[j] == target {
print(numbers[i],numbers[j])
result.append(i)
result.append(j)
}
}
}
print(Array(Set(result)))
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var dict:[Int:Int] = [:]
for i in 0..<nums.count {
if dict[target - nums[i]] != nil {
return [dict[target - nums[i]] ?? 0, i]
} else {
dict[nums[i]] = i
}
}
return [0]
}
Here is a link to the discussion section of the TwoSum problem on Leetcode.
Lots of great Swift solutions there.
https://leetcode.com/problems/two-sum/discuss/?currentPage=1&orderBy=most_votes&query=swift.
My personal two cents -
func twoSumA(_ nums: [Int], _ target: Int) -> [Int] {
var numsHashMap: Dictionary<Int, Int> = [:]
var outputArr: [Int] = []
for index in 0..<nums.count {
let currentNum = nums[index]
if numsHashMap.keys.contains(target-currentNum) {
outputArr.append(numsHashMap[target-currentNum] ?? -1)
outputArr.append(index)
return outputArr
}
numsHashMap[currentNum] = index
}
return !outputArr.isEmpty ? outputArr : [-1, -1]
}

How to split or iterate over an Int without converting to String in Swift [duplicate]

This question already has answers here:
Break A Number Up To An Array of Individual Digits
(6 answers)
Closed 5 years ago.
I was wondering if there was a way in Swift to split an Int up into it's individual digits without converting it to a String. For example:
let x: Int = 12345
//Some way to loop/iterate over x's digits
//Then map each digit in x to it's String value
//Return "12345"
For a bit of background, I'm attempting to create my own method of converting an Int to a String without using the String description property or using String Interpolation.
I've found various articles on this site but all the ones I've been able to find either start with a String or end up using the String description property to convert the Int to a String.
Thanks.
Just keep dividing by 10 and take the remainder:
extension Int {
func digits() -> [Int] {
var digits: [Int] = []
var num = self
repeat {
digits.append(num % 10)
num /= 10
} while num != 0
return digits.reversed()
}
}
x.digits() // [1,2,3,4,5]
Note that this will return all negative digits if the value is negative. You could add a special case if you want to handle that differently. This return [0] for 0, which is probably what you want.
And because everyone like pure functional programming, you can do it that way too:
func digits() -> [Int] {
let partials = sequence(first: self) {
let p = $0 / 10
guard p != 0 else { return nil }
return p
}
return partials.reversed().map { $0 % 10 }
}
(But I'd probably just use the loop here. I find sequence too tricky to reason about in most cases.)
A recursive way...
extension Int {
func createDigitArray() -> [Int] {
if self < 10 {
return [self]
} else {
return (self / 10).createDigitArray() + [self % 10]
}
}
}
12345.createDigitArray() //->[1, 2, 3, 4, 5]
A very easy approach would be using this function:
func getDigits(of number: Int) -> [Int] {
var digits = [Int]()
var x = number
repeat{
digits.insert(abs(x % 10), at: 0)
x/=10
} while x != 0
return digits
}
And using it like this:
getDigits(of: 97531) // [9,7,5,3,1]
getDigits(of: -97531) // [9,7,5,3,1]
As you can see, for a negative number you will receive the array of its digits, but at their absolute value (e.g.: -9 => 9 and -99982 => 99982)
Hope it helps!

Group dictionary by key in Swift

I'm trying to implement a groupBy functionality where all the numbers of a nested list are grouped. My code so far:
struct MyClass {
var numbers: [Int]
...
}
var dict: [String : MyClass] = ...
let numbers = dict
.filter{ $0.0.containsString(searchString) }
.flatMap{ $0.1.numbers }
This yields me an Array of Ints. However I'd like to have a dictionary [Int : Int] with each unique number and the count of its occurence. So for example:
[1,2,3,4,1,2,2,1]
should be:
[1 : 2, 2 : 3, 3 : 1, 4 : 1]
I know there's a groupBy operator, but Swift doesn't seem to have one. I've tried with reduce:
func reducer(accumulator: [Int: Int], num: Int) -> [Int : Int] {
var acc = accumulator
acc[num]! += 1
return acc
}
filtered.reduce([:], combine: reducer)
But it crashes when I want to run it. Not sure why, I get a EXC_BAD_INSTRUCTION.
I'd appreciate any help.
let numbers = [1,2,3,4,1,2,2,1]
var results = [Int: Int]()
Set(numbers).forEach { number in results[number] = numbers.filter { $0 == number }.count }
print(results) // [2: 3, 3: 1, 1: 3, 4: 1]
Actually I'm not very sure if this is what you want. I just looked at your examples.
Using NSCountedSet:
var objects = [1,2,3,4,1,2,2,1]
let uniques = NSCountedSet(array: objects)
uniques.forEach { results[$0 as! Int] = uniques.countForObject($0) }
print(results) // [2: 3, 3: 1, 1: 3, 4: 1]
I would expect the crash to be ocurring on this line:
acc[num]! += 1
The first time this is called for a number, the entry doesn't exist in the dictionary yet so acc[num] is nil. Forcefully unwrapping it would cause a crash.
Not sure if this is the best solution but you can simple check for this case:
if (acc[num]) {
acc[num]! += 1
} else {
acc[num] = 1
}
Cleaner code from #vacawama in the comments:
acc[num] = (acc[num] ?? 0) + 1
Here's an extension to Array that does what you're asking:
extension Array where Element: Hashable {
var grouped: [Element:Int] {
var dict = [Element:Int]()
self.forEach { dict[$0] = (dict[$0] ?? 0) + 1 }
return dict
}
}
The key is the closure: { dict[$0] = (dict[$0] ?? 0) + 1 }.
It takes the current value in the array, tests to see if it's a key in the dictionary, returns the value for that key if it exists or 0 if it doesn't, then adds one and sets the key:value to be the pair of the current value and occurrences so far.
Example use:
[1,2,3,4,1,2,2,1].grouped // => [2: 3, 3: 1, 1: 3, 4: 1]
You need something like this:
if let _ = acc.indexForKey(num) {
acc[num]! += 1
}
else {
acc[num] = 1
}
It's sort of unclear what you're asking for, but here's a function that will take an array of ints and return a dictionary with the number as the key, and the count as the value:
func getDictionaryOfCounts(accumulator: [Int]) -> [Int : Int] {
var countingDictionary: [Int : Int] = [:]
accumulator.forEach { (value) in
if countingDictionary[value] != nil {
countingDictionary[value]! += 1
}
else{
countingDictionary[value] = 1
}
}
return countingDictionary
}

Change specific values in an array

I have 3 arrays of type Int that have 5 values in each in them. I'm trying to create a function that replaces the values based on conditions. For example, array1, if a value of an index is between 2-5, replace with a randomly generated number between 1-6.
Here is what I have so far
import Foundation
func newRandomNumbers(#array1: [Int], array2: [Int], array3: [Int]) {
for i in 0..<5 {
switch (array1[i]) {
case 2, 3, 4, 5:
let randomNumber = Int(1 + arc4random() % 6)
array1[i] = randomNumber
break;
default:
break;
}
switch (array2[i]) {
case 2, 3, 4, 5:
array2[i]
break;
default:
break;
}
switch (array3[i]) {
case 1, 2, 3, 4, 5:
array3[i]
break;
default:
break;
}
}
}
I get an error " Cannot assign to immutable value of type 'Int'"
When I rewrite a simplified function that does the same purpose I don't get the error, but strangely not all the numbers are replaced.
import Foundation
var newArray = [2,3,4,5,6]
func newRandom(#array1: [Int]){
for i in 0..<5 {
switch(array1[i]) {
case 2, 3, 4, 5:
let randomNumber = Int(1 + arc4random() % 6)
newArray[1] = randomNumber
default:
break;
}
}
}
newRandom(array1: newArray)
newArray
Not sure how to resolve. Recommendations on how to clean up my code would also be appreciated. Thanks!
"Cannot assign to immutable value of type 'Int'" is because the parameters are actually constants. You can read the line:
func newRandomNumbers(#array1: [Int], array2: [Int], array3: [Int])
as:
func newRandomNumbers(#array1 let array1: [Int], let array2: [Int], let array3: [Int])
You can change it by putting var in front of the parameter and return the array. Another option is using & to make it a reference, so you don't have to return a new array.
I would suggest you look into the array's map function:
https://developer.apple.com/library/ios/documentation/General/Reference/SwiftStandardLibraryReference/Array.html
you would use for example:
array.map {
if ($0 satisfies condition) {
generate random number
}
}
You can use inout for each of your function parameters and add & before your variable name when calling the function, it will modify the array in place:
func newRandom(inout myArray: [Int]) {
for (index, element) in enumerate(myArray) {
switch element {
case 2...5:
myArray[index] = Int(1 + arc4random() % 6)
default:
break
}
}
}
var newArray = [2,3,4,5,6]
newRandom(&newArray)
println(newArray) // "newArray" was modified, contains random numbers
Note:
switch element {
case 2...5:
is equivalent to:
if element >= 2 && element <= 5 {
SWIFT 2.0 UPDATE:
As of Swift 2.0, enumerate is now to be called on the sequence itself. Also, println has been deprecated in favor of print:
func newRandom(inout myArray: [Int]) {
for (index, element) in myArray.enumerate() {
switch element {
case 2...5:
myArray[index] = Int(1 + arc4random() % 6)
default:
break
}
}
}
var newArray = [2,3,4,5,6]
newRandom(&newArray)
print(newArray)