using an array to subset an array in Swift4 - arrays

This must be a really basic question. In languages like R you can take an array (swift syntax here)
let x = [1,2,3,4,5]
and extract multiple elements using an array of indices. That is I would like to be able to do something like say (now in a pseudo-Swift syntax because it does not parse)
x[[0,2,3]]
to get a return value of
[1,3,4]
but this does not work directly in Swift. Is there a standard way of doing this? I am currently using Swift4.

I'm not aware of anything built into the Swift Array class that does this.
One possible solution is to define an extension to Array that filters the array to only include the elements at the provided indices.
extension Array {
func elements(at indices: [Int]) -> Array<Element> {
return self.enumerated().filter { indices.contains($0.0) }.map { $0.1 }
}
}
Example usage:
let x = [1,2,3,4,5]
let res = x.elements(at: [0,2,3])
print(res)
Output:
[1, 3, 4]

Related

How to enumerate a zip array in swift 4 to get index for each object

I need to enumerate a zipped array to check index for each object from array. Below is my code please help me out.
for((index1, index2),(value1, value2)) in zip(array1, array2).enumerated() {
// INDEX OF OBJECT
}
Once you zip the array it is having only one index. Just use it as normal enumerations.
let arr1 = ["1","2","3"]
let arr2 = ["A","B"]
let arr3 = zip(arr1, arr2)
print(arr3)
for (index, (x,y)) in arr3.enumerated() {
print(index, y)
}
enjoy!
You can make use of enumerated to know index of value like this way.
func zipArray() {
let words = ["one", "two", "three", "four"]
let numbers = 1...4
let zipArray = Array(zip(words, numbers))
print(zipArray)
for (index, tuple) in zipArray.enumerated() {
print("Index : \(index): Word :\(tuple.0) Number : \(tuple.1)")
}
}
Once zip perform on two array then it returns another array with pair of inputs.
So you can perform enumerated operation on zipped array using above code and get index and value as tuple. Then for individual element of tuple index would be same.
There may be better way. But this will fulfill your purpose of index.

Check if value is in the array as dictionary value?

I am looking for a way to see if a value (stored in a dictionary) is in an array. The array looks like this: var array = Array<Dictionary<String, Int>>()
I looked around here, on stackoverflow, and found this: How to check if an element is in an array. Only problem is I can't seem to use the contains method by writing arrray.contains.... What am I doing wrong?
You can use contains within contains to check if any of the dictionaries in an array contains the value you are looking for.
For example, to search array a for the value 1:
let a: [[String: Int]] = [["a": 1, "b": 2], ["c": 3], ["d": 4]]
Swift 1.2:
if contains(a, {contains($0.values, 1)}) {
println("it is in there")
}
Swift 2.0:
/* Thanks to #Qbyte for providing this solution */
if a.contains({ $0.values.contains(1) }) {
print("it is in there")
}
This works because each member of array a gets evaluated with the closure {contains($0.values), 1} to see if it is true. The closure takes the dictionary it is passed, gets the array of values from it and then uses contains to see if the value is in there.
You can use this extension:
extension Array {
func contains<T where T : Equatable>(obj: T) -> Bool {
return self.filter({$0 as? T == obj}).count > 0
}
}
Which you can test like:
var array1 = Array<Dictionary<String, Int>>()
array1 = [["zero": 0],["one": 1], ["two": 2]]
array1.contains(["zero": 0]) //true
array1.contains(["five": 5]) //false

swift getting an array from something like array[0..<10]

I want to get a range of objects from an array. Something like this:
var array = [1,3,9,6,3,4,7,4,9]
var newArray = array[1...3] //[3,9,6]
The above would access elements from index 1 to 3.
Also this:
newArray = array[1,5,3] // [3,4,6] would be cool
This would retrieve elements from index 1, 5 and 3 respectively.
That last example can be achieved using PermutationGenerator:
let array = [1,3,9,6,3,4,7,4,9]
let perms = PermutationGenerator(elements: array, indices: [1,5,3])
// perms is now a sequence of the values in array at indices 1, 5 and 3:
for x in perms {
// iterate over x = 3, 4 and 6
}
If you really need an array (just the sequence may be enough for your purposes) you can pass it into Array's init method that takes a sequence:
let newArray = Array(perms)
// newArray is now [3, 4, 6]
For your first example - with arrays, that will work as-is. But it looks from your comments like you're trying it with strings as well. Strings in Swift are not random-access (for reasons relating to unicode). So you can't use integers, they have an String-specific bidirectional index type:
let s = "Hello, I must be going"
if let i = find(s, "I") {
// prints "I must be going"
println(s[i..<s.endIndex])
}
This works :
var n = 4
var newArray = array[0..<n]
In any case in
Slicing Arrays in Swift you'll find a very nice sample of the Python slice using a extension to Arrays in Swift.

Unwrap Sparse Array in Swift

I'd like to write an extension for Array which safely returns an unwrapped version of itself.
I can do it with a generic method like so:
func unwrapElements<T>(array: [T?]) -> [T] {
let filtered: [T?] = array.filter{ $0 != nil }
let unwrapped: [T] = filtered.map { $0! }
return unwrapped
}
And I can call it like this:
let sparseNames: [String?] = ["alice", "bob", nil, "doug", nil, nil, "george", "hubert"]
let names: [String] = unwrapElements(sparseNames)
where names ends up being ["alice", "bob", "doug", "george", "hubert"] and is safe to iterate and work with each element.
However, I want to call it like this:
let names = sparseNames.unwrapElements()
I've seen a few similar questions (like this one) but they don't address how to create the method as an extension.
(this is tagged with Xcode6.1 to denote the version of Swift I'm using)
Note: Swift 1.2 Beta 3 has introduced the flatMap function which helps with optionally chaining arrays. See this excellent blog post here
You can't do this right now in Swift. To add that function as an extension to Array, you'd have to mark somehow that it's only callable with certain kinds of arrays: those with optional values as the subtype. Unfortunately, you can't further specialize a generic type, so global functions are the only way possible.
This is the same reason Array has a sort method that takes a comparison function as a parameter, but it doesn't have a sort that "just works" if the array is full of Comparable members - to get that kind of function, you have to look at the top-level sort:
func sort<T : Comparable>(inout array: [T])
Have you tried using filter and map for that?
let array: [String?] = ["Hello", nil, "World"]
let unwrapped = array.map{$0 ?? nil}.filter{$0 != nil}.map{$0!}
println("unwrapped: \(unwrapped)")
// prints "unwrapped: [Hello, World]"
The first map uses the Nil Coalescing Operator to unwrap if possible. Although, I return nil regardless since the following filter removes all nil values. The last map does the actual unwrapping.
You can do this. Here is how:
extension Array {
func catOptionals<A>() -> [A] where Element == A? {
return self.flatMap{ $0 }
}
}

swift: modifying arrays inside dictionaries

How can I easily add elements to an array inside a dictionary?
It's always complaining with could not find member 'append' or could not find an overload for '+='
var dict = Dictionary<String, Array<Int>>()
dict["key"] = [1, 2, 3]
// all of these fail
dict["key"] += 4
dict["key"].append(4) // xcode suggests dict["key"].?.append(4) which also fails
dict["key"]!.append(4)
dict["key"]?.append(4)
// however, I can do this:
var arr = dict["key"]!
arr.append(4) // this alone doesn't affect dict because it's a value type (and was copied)
dict["key"] = arr
if I just assign the array to a var, modify it and then reassign it to the dict, won't I be copying everything? that wouldn't be efficient nor elegant.
Swift beta 5 has added this functionality, and you've nailed the new method in a couple of your attempts. The unwrapping operators ! and ? now pass through the value to either operators or method calls. That is to say, you can add to that array in any of these ways:
dict["key"]! += [4]
dict["key"]!.append(4)
dict["key"]?.append(4)
As always, be careful about which operator you use -- force unwrapping a value that isn't in your dictionary will give you a runtime error:
dict["no-key"]! += [5] // CRASH!
Whereas using optional chaining will fail silently:
dict["no-key"]?.append(5) // Did it work? Swift won't tell you...
Ideally you'd be able to use the new null coalescing operator ?? to address this second case, but right now that's not working.
Answer from pre-Swift beta 5:
It's a quirk of Swift that it's not possible to do what you're trying to do. The issue is that the value of any Optional variable is in fact a constant -- even when forcibly unwrapping. If we just define an Optional array, here's what we can and can't do:
var arr: Array<Int>? = [1, 2, 3]
arr[0] = 5
// doesn't work: you can't subscript an optional variable
arr![0] = 5
// doesn't work: constant arrays don't allow changing contents
arr += 4
// doesn't work: you can't append to an optional variable
arr! += 4
arr!.append(4)
// these don't work: constant arrays can't have their length changed
The reason you're having trouble with the dictionary is that subscripting a dictionary returns an Optional value, since there's no guarantee that the dictionary will have that key. Therefore, an array in a dictionary has the same behavior as the Optional array, above:
var dict = Dictionary<String, Array<Int>>()
dict["key"] = [1, 2, 3]
dict["key"][0] = 5 // doesn't work
dict["key"]![0] = 5 // doesn't work
dict["key"] += 4 // uh uh
dict["key"]! += 4 // still no
dict["key"]!.append(4) // nope
If you need to change something in an array in the dictionary you'll need to get a copy of the array, change it, and reassign, like this:
if var arr = dict["key"] {
arr.append(4)
dict["key"] = arr
}
ETA: Same technique works in Swift beta 3, though constant arrays no longer allow changes to contents.
The accepted answer bypasses the following much simpler possibility, which also works for older Swift versions:
var dict = Dictionary<String, Array<Int>>()
dict["key"] = [1, 2, 3]
print(dict)
dict["key", default: [Int]()].append(4)
print(dict)
This will print:
["key": [1, 2, 3]]
["key": [1, 2, 3, 4]]
And this:
var dict = Dictionary<String, Array<Int>>()
dict["key", default: [Int]()].append(4)
print(dict)
will print:
["key": [4]]
As a simple workaround you can use a NSMutableArray:
import Foundation
var dict = Dictionary<String, NSMutableArray>()
dict["key"] = [1, 2, 3] as NSMutableArray
dict["key"]!.addObject(4)
I am using effectively such simple solution in my project:
https://github.com/gui-dos/Guigna/blob/5c02f7e70c8ee3b2265f6916c6cbbe5cd3963fb5/Guigna-Swift/Guigna/GuignaAppDelegate.swift#L1150-L1157
Here is what I was telling Nate Cook, in the comments for his quality answer. This is what I consider "easily [adding] elements to an array inside a dictionary":
dict["key"] = dict["key"]! + 4
dict["key"] = dict["key"] ? dict["key"]! + 4 : [4]
For now, we need to define the + operator ourselves.
#infix func +<T>(array: T[], element: T) -> T[] {
var copy = array
copy += element
return copy
}
I think this version removes too much safety; maybe define it with a compound operator?
#infix func +<T>(array: T[]?, element: T) -> T[] {
return array ? array! + element : [element]
}
dict["key"] = dict["key"] + 4
Finally, this is the cleanest I can get it, but I'm confused about how array values/references work in this example.
#assignment func +=<T>(inout array: T[]?, element: T) {
array = array + element
}
dict["key"] += 5
Use 'for in', for getting values from dictionary's inside array. Here's an example to help you
var a = ["x":["a","b","c"], "y":["d"]]
for b in a {
print(b)
}
Output:
("x", ["d"])
("y", ["a", "b", "c"])

Resources