Swift convert [String?] to [String!] - arrays

I tested in Swift 3.0. I want to add array1 to array2, example and errors as below:
var array1: [String?] = ["good", "bad"]
var array2 = [String!]()
array2.append(array1)
//Cannot convert value of type '[String?]' to expected argument type 'String!'
array2.append(contentsOf: array1)
//Extraneous argument label 'contentsOf:'in call
I know if I change to
var array2 = [String?]()
array2.append(contentsOf: array1)
it works!
How should I fix this if i don't change type?

In Swift 3 you cannot define an array where the generic element is an implicitly unwrapped optional.
Implicitly unwrapped optionals are only allowed at top level and as function results.
The compiler
What you can do is creating a new array of String containing only the populated elements of array1.
let array1: [String?] = ["good", "bad", nil]
let array2: [String] = array1.flatMap { $0 }
print(array2) // ["good", "bad"]
Update
As shown by Sam M this is indeed possible, here's the code
let array2 = array1.map { elm -> String! in
let res: String! = elm
return res
}

var array1: [String?] = ["good", "bad"]
var array2 = [String!]()
var array2a = [String]()
for item in array1 {
array2.append(item)
}
for item in array1 {
array2a.append(item!)
}
print("1", array1)
print("2", array2)
print("2a", array2a)
Prints:
1 [Optional("good"), Optional("bad")]
2 [good, bad]
2a ["good", "bad"]
Mapping also works, e.g.:
array2 = array1.map{ $0 }
array2a = array1.filter{ $0 != nil }.map{ $0! }

Related

merging element of two different arrays into dictionary in swift

i have two arrays like these
var arr1 = ["han", "Ji", "Kidda", "Ho", "Tusi"]
var arr2 = ["hello", "Ji"]
i want to create a new dictionary that have first element of first array and first element of second array and so on. when the third element of first array comes it should again get the first element of second array.
for example:-
dict = ["han" : "hello", "Ji" : "Ji", "Kidda" : hello, "Ho" : "Ji", "Tusi" : "hello"]
If the second array has 2 items you can do
var dict = [String: String]()
for (index, item) in arr1.enumerated() {
dict[item] = arr2[index % 2]
}
I believe this is what you're looking for (using arr1 as the keys and arr2 as the values repeating them as necessary):
var arr1 = ["han", "Ji", "Kidda", "Ho", "Tusi"]
var arr2 = ["hello", "Ji"]
let dict = Dictionary(uniqueKeysWithValues: zip(arr1, arr1.indices.map { arr2[$0 % arr2.count] }))
print(dict)
["Kidda": "hello", "Ji": "Ji", "han": "hello", "Ho": "Ji", "Tusi": "hello"]
Note:
Dictionaries have no specified ordering. Only the key/value pairings matter. This matches the example in your question.
Explanation:
zip is used to create a sequence of (key, value) tuples from two sequences that will become the key/value pairs for the new Dictionary. The keys come from arr1. map is used to generate the sequence of values from arr2 repeating them as many times as necessary to match the count of arr1. This sequence of (key, value) tuples is passed to Dictionary(uniqueKeysWithValues:) to turn that sequence into the desired Dictionary.
try:
var dict = ["arr1" : "hello", "arr2" : "Ji"]
then for third you can append by
dict[3] = ["arr3" : String(arr3.first())]
Try this:
var arr1 = ["han", "Ji", "Kidda", "Ho", "Tusi"]
var arr2 = ["hello", "Ji"]
var dict : [String : String] = [:]
var arr2Index = 0
for index in 0..<arr1.count {
let arr1Value = arr1[index]
if arr2Index == arr2.count {
arr2Index = 0
}
let arr2Value = arr2[arr2Index]
dict[arr1Value] = arr2Value
arr2Index += 1
}
Here's a fun way:
let arr1 = ["han", "Ji", "Kidda", "Ho", "Tusi"]
let arr2 = ["hello", "Ji"]
let arr3 = Array(repeating: arr2, count: arr1.count).joined()
let d = zip(arr1,arr3).reduce(into: [String:String]()) { $0[$1.0] = $1.1 }

Use of unresolved identifier 'finalArray'?

var firstArray = ["1.","2.","3.","4."]
var secondArray = ["a","b","c"]
func combineTheArrays(array1: [Any], array2: [Any]) -> [Any] {
var finalArray = [Any]()
let maxIndex = array1.count >= array2.count ? array1.count : array2.count;
for i in 0...maxIndex{
if (array1.count > i){
finalArray.append(array1[i])
}
if (array2.count > i){
finalArray.append(array2[i])
}
} }
combineTheArrays(array1: firstArray, array2: secondArray)
print(finalArray)
I am trying to take two arrays with different/similar types and have it work through the function and combine into one single array. The ideal result of this func is to print:
finalArray = ["1.", "a", "2.", "b", "3.", "c", "4."]
You're very close! You just need to return finalArray at the end of your function definition, and then assign the result of the function call, so that you can then use it (such as in a print call):
let finalArray = combineTheArrays(array1: firstArray, array2: secondArray)
print(finalArray)
You should also use generics to ensure that you can handle any kind of elements, so long as their types are the same. Unlike returning Any, your result will be an array of the same type, which will be safer and easier to work with. Here is how I would improve this code:
func combineTheArrays<T>(array1: [T], array2: [T]) -> [T] {
let maxIndex = max(array1.count, array2.count);
var finalArray = [T]()
finalArray.reserveCapacity(array1.count + array2.count)
for i in 0..<maxIndex {
if i < array1.count { finalArray.append(array1[i]) }
if i < array2.count { finalArray.append(array2[i]) }
}
return finalArray
}
var firstArray = ["1.","2.","3.","4."]
var secondArray = ["a","b","c"]
let finalArray = combineTheArrays(array1: firstArray, array2: secondArray)
print(finalArray)

How do I do indexOf for arrays with optionals?

In Swift2, how can you do indexOf on arrays that contain optionals? The following example won't compile:
var strings: [String?] = ["hello", nil, "world"]
let index = strings.indexOf("world")
with the compiler complaining
"Cannot invoke indexOf with an argument list of type '(String)'"
A naive approach without indexOf would be:
let index: Int = {
for var i = 0; i < strings.count; i++ {
let s: String? = strings[i]
if s == "world" {
return i
}
}
return -1
}()
Isn't there any way to use the built-in indexOf function?
The same example however works for arrays with non-optionals:
var strings: [String] = ["hello", "world"]
let index = str.indexOf("world")
Or even simpler:
let strings: [String?] = ["hello", nil, "world"]
strings.indexOf{ $0 == "world" }
This works because == is defined for optionals as well
You can use an expression in "trailing closure" for indexOf:
var arr: [String?] = ["hello", nil, "world"]
let i = arr.indexOf() { $0 != nil && $0 == "world" }

In Swift, what's the cleanest way to get the last two items in an Array?

Is there a cleaner way to get the last two items of an array in Swift? In general, I try to avoid this approach since it's so easy to be off-by-one with the indexes. (Using Swift 1.2 for this example.)
// Swift -- slices are kind of a hassle?
let oneArray = ["uno"]
let twoArray = ["uno", "dos"]
let threeArray = ["uno", "dos", "tres"]
func getLastTwo(array: [String]) -> [String] {
if array.count <= 1 {
return array
} else {
let slice: ArraySlice<String> = array[array.endIndex-2..<array.endIndex]
var lastTwo: Array<String> = Array(slice)
return lastTwo
}
}
getLastTwo(oneArray) // ["uno"]
getLastTwo(twoArray) // ["uno", "dos"]
getLastTwo(threeArray) // ["dos", "tres"]
I was hoping for something closer to Python's convenience.
## Python -- very convenient slices
myList = ["uno", "dos", "tres"]
print myList[-2:] # ["dos", "tres"]
With Swift 5, according to your needs, you may choose one of the following patterns in order to get a new array from the last two elements of an array.
#1. Using Array's suffix(_:)
With Swift, objects that conform to Collection protocol have a suffix(_:) method. Array's suffix(_:) has the following declaration:
func suffix(_ maxLength: Int) -> ArraySlice<Element>
Returns a subsequence, up to the given maximum length, containing the final elements of the collection.
Usage:
let array = [1, 2, 3, 4]
let arraySlice = array.suffix(2)
let newArray = Array(arraySlice)
print(newArray) // prints: [3, 4]
#2. Using Array's subscript(_:)
As an alternative to suffix(_:) method, you may use Array's subscript(_:) subscript:
let array = [1, 2, 3, 4]
let range = array.index(array.endIndex, offsetBy: -2) ..< array.endIndex
//let range = array.index(array.endIndex, offsetBy: -2)... // also works
let arraySlice = array[range]
let newArray = Array(arraySlice)
print(newArray) // prints: [3, 4]
myList[-2:]
Yes, I have an enhancement request filed asking for negative index notation, and I suggest you file one too.
However, you shouldn't make this harder on yourself than you have to. The built-in global suffix function does exactly what you're after:
let oneArray = ["uno"]
let twoArray = ["uno", "dos"]
let threeArray = ["uno", "dos", "tres"]
let arr1 = suffix(oneArray,2) // ["uno"]
let arr2 = suffix(twoArray,2) // ["uno", "dos"]
let arr3 = suffix(threeArray,2) // ["dos", "tres"]
The result is a slice, but you can coerce it to an Array if you need to.
in swift 5 you can use suffix for get objects from the last and use prefix for get objects from the first, here is an example:
let exampleArray = ["first text", "second text", "third text"]
let arr1 = exampleArray.suffix(2) // ["second text", "third text"]
let arr2 = exampleArray.prefix(2) // ["first text", "second text"]
The result is a slice, but you can coerce it to an Array if you need to.
In Swift 2, you can extend CollectionType. Here's an example (borrowing from Rob Napier's answer):
extension CollectionType {
func last(count:Int) -> [Self.Generator.Element] {
let selfCount = self.count as! Int
if selfCount <= count - 1 {
return Array(self)
} else {
return Array(self.reverse()[0...count - 1].reverse())
}
}
}
You can use it on any CollectionType. Here's Array:
let array = ["uno", "dos", "tres"]
print(array.last(2)) // [dos, tres]
Here's CharacterView:
let string = "looking"
print(string.characters.last(4)) // [k, i, n, g]
(Note that my example returns an Array in all cases, not the original collection type.)
More generic answer ...
let a1 = [1,2,3,4,5]
let a2 = ["1","2","3","4","5"]
func getLast<T>(array: [T], count: Int) -> [T] {
if count >= array.count {
return array
}
let first = array.count - count
return Array(array[first..<first+count])
}
getLast(a1, count: 2) // [4, 5]
getLast(a2, count: 3) // ["3", "4", "5"]
the last two items of an array in Swift
EDIT: first checks that myArray.count >= 2
let myArray2:Array? = myArray.count >= 2 ? [myArray[myArray.count-2], myArray[myArray.count-1]] : nil
Here it is wrapped in a function which takes the array and either returns an array containing the last two or else returns nil if the passed array does not contain at least two items.
func getLastTwo(myArray:[String]) -> [String]? {
return myArray.count >= 2 ? [myArray[myArray.count-2], myArray[myArray.count-1]] : nil
}
I doubt it's going to make you that much happier, but the math is certainly simpler:
func getLastTwo(array: [String]) -> [String] {
if array.count <= 1 {
return array
} else {
return array.reverse()[0...1].reverse()
}
}
Note that reverse() is lazy, so this isn't particularly expensive.
let items = [0, 2, 5, 3, 7, 6, 9, 10]
let count = items.count
let last2 = items[count - 2 ..< count] // [9, 10]
Swift4 solution:
let oneArray = ["uno"]
let twoArray = ["uno", "dos"]
let threeArray = ["uno", "dos", "tres"]
let arr1 = threeArray.suffix(from: threeArray.count-2) // ["dos", "tres"]
Other examples to clarify the functionality of Swift's built in function func suffix(from start: Int) -> ArraySlice<Element> are...
let arr2 = oneArray.suffix(from: 0) // ["uno"]
let arr3 = twoArray.suffix(from: 0) // ["uno", "dos"]
let arr4 = twoArray.suffix(from: 1) // ["dos"]
let arr5 = threeArray.suffix(from: 1) // ["dos", "tres"]
let arr6 = threeArray.suffix(from: 2) // ["tres"]

Swift 3 - Function for Wrapping an Array

As I try to learn Swift 3, I am attempting to "Wrap an Array of Elements". In Swift 3 Playgrounds, I was able to implement code that wraps an array. My problem occurs when I try to create a function that implements my code.
If you take the following code and copy-paste it to a Swift 3 Playground then you will likely see what I am trying to do. Change the selectedElement and on the right you will see the correct newArray. You can interchange the different elements for the selectedElement and the newArray will change accordingly.
I noted one of my failed attempts at turning this into a function.
import UIKit
let myArray = ["a", "b", "c", "d", "e"]
let selectedElement = "a"
//func arrayWrapper(inputArray: Array<String>) -> Array<String> {
var oldArray = [String]()
var priorElements = [String]()
var newArray = [String]()
for element in myArray {
if element == selectedElement || oldArray.count > 0 {
oldArray.append(element)
} else {
priorElements.append(element)
}
newArray = oldArray + priorElements
}
//return newArray
//}
priorElements
oldArray
oldArray + priorElements
newArray
Your method works fine.
The only problem I see is that you are trying to access the variables created inside the function outside of the function scope.
//priorElements
//oldArray
//
//oldArray + priorElements
arrayWrapper(inputArray: myArray)
Well, your non-function solution to this problem takes two inputs - the array you want to wrap, and the element you want it to wrap at. Therefore, your function should have two parameters as well:
// note the second parameter
func arrayWrapper(inputArray: Array<String>, selectedElemented: String) -> Array<String> {
var oldArray = [String]()
var priorElements = [String]()
var newArray = [String]()
for element in myArray {
if element == selectedElement || oldArray.count > 0 {
oldArray.append(element)
} else {
priorElements.append(element)
}
newArray = oldArray + priorElements
}
return newArray
}
Here is a more general version of this function, as an extension of Array:
extension Array where Element : Equatable {
func wrap(around selectedElement: Element) -> Array<Element> {
var oldArray = [Element]()
var priorElements = [Element]()
var newArray = [Element]()
for element in self {
if element == selectedElement || oldArray.count > 0 {
oldArray.append(element)
} else {
priorElements.append(element)
}
newArray = oldArray + priorElements
}
return newArray
}
}
// usage
myArray.wrap(around: selectedElement)

Resources