Array return optional value? [duplicate] - ios

If I have an array in Swift, and try to access an index that is out of bounds, there is an unsurprising runtime error:
var str = ["Apple", "Banana", "Coconut"]
str[0] // "Apple"
str[3] // EXC_BAD_INSTRUCTION
However, I would have thought with all the optional chaining and safety that Swift brings, it would be trivial to do something like:
let theIndex = 3
if let nonexistent = str[theIndex] { // Bounds check + Lookup
print(nonexistent)
...do other things with nonexistent...
}
Instead of:
let theIndex = 3
if (theIndex < str.count) { // Bounds check
let nonexistent = str[theIndex] // Lookup
print(nonexistent)
...do other things with nonexistent...
}
But this is not the case - I have to use the ol' if statement to check and ensure the index is less than str.count.
I tried adding my own subscript() implementation, but I'm not sure how to pass the call to the original implementation, or to access the items (index-based) without using subscript notation:
extension Array {
subscript(var index: Int) -> AnyObject? {
if index >= self.count {
NSLog("Womp!")
return nil
}
return ... // What?
}
}

Alex's answer has good advice and solution for the question, however, I've happened to stumble on a nicer way of implementing this functionality:
extension Collection {
/// Returns the element at the specified index if it is within bounds, otherwise nil.
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
Example
let array = [1, 2, 3]
for index in -20...20 {
if let item = array[safe: index] {
print(item)
}
}

If you really want this behavior, it smells like you want a Dictionary instead of an Array. Dictionaries return nil when accessing missing keys, which makes sense because it's much harder to know if a key is present in a dictionary since those keys can be anything, where in an array the key must in a range of: 0 to count. And it's incredibly common to iterate over this range, where you can be absolutely sure have a real value on each iteration of a loop.
I think the reason it doesn't work this way is a design choice made by the Swift developers. Take your example:
var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0] )"
If you already know the index exists, as you do in most cases where you use an array, this code is great. However, if accessing a subscript could possibly return nil then you have changed the return type of Array's subscript method to be an optional. This changes your code to:
var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0]! )"
// ^ Added
Which means you would need to unwrap an optional every time you iterated through an array, or did anything else with a known index, just because rarely you might access an out of bounds index. The Swift designers opted for less unwrapping of optionals, at the expense of a runtime exception when accessing out of bounds indexes. And a crash is preferable to a logic error caused by a nil you didn't expect in your data somewhere.
And I agree with them. So you won't be changing the default Array implementation because you would break all the code that expects a non-optional values from arrays.
Instead, you could subclass Array, and override subscript to return an optional. Or, more practically, you could extend Array with a non-subscript method that does this.
extension Array {
// Safely lookup an index that might be out of bounds,
// returning nil if it does not exist
func get(index: Int) -> T? {
if 0 <= index && index < count {
return self[index]
} else {
return nil
}
}
}
var fruits: [String] = ["Apple", "Banana", "Coconut"]
if let fruit = fruits.get(1) {
print("I ate a \( fruit )")
// I ate a Banana
}
if let fruit = fruits.get(3) {
print("I ate a \( fruit )")
// never runs, get returned nil
}
Swift 3 Update
func get(index: Int) ->T? needs to be replaced by func get(index: Int) ->Element?

To build on Nikita Kukushkin's answer, sometimes you need to safely assign to array indexes as well as read from them, i.e.
myArray[safe: badIndex] = newValue
So here is an update to Nikita's answer (Swift 3.2) that also allows safely writing to mutable array indexes, by adding the safe: parameter name.
extension Collection {
/// Returns the element at the specified index if it is within bounds, otherwise nil.
subscript(safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
extension MutableCollection {
subscript(safe index: Index) -> Element? {
get {
return indices.contains(index) ? self[index] : nil
}
set(newValue) {
if let newValue = newValue, indices.contains(index) {
self[index] = newValue
}
}
}
}

extension Array {
subscript (safe index: Index) -> Element? {
0 <= index && index < count ? self[index] : nil
}
}
O(1) performance
type safe
correctly deals with Optionals for [MyType?] (returns MyType??, that can be unwrapped on both levels)
does not lead to problems for Sets
concise code
Here are some tests I ran for you:
let itms: [Int?] = [0, nil]
let a = itms[safe: 0] // 0 : Int??
a ?? 5 // 0 : Int?
let b = itms[safe: 1] // nil : Int??
b ?? 5 // nil : Int? (`b` contains a value and that value is `nil`)
let c = itms[safe: 2] // nil : Int??
c ?? 5 // 5 : Int?

Swift 4
An extension for those who prefer a more traditional syntax:
extension Array {
func item(at index: Int) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}

Valid in Swift 2
Even though this has been answered plenty of times already, I'd like to present an answer more in line in where the fashion of Swift programming is going, which in Crusty's words¹ is: "Think protocols first"
• What do we want to do?
- Get an Element of an Array given an Index only when it's safe, and nil otherwise
• What should this functionality base it's implementation on?
- Array subscripting
• Where does it get this feature from?
- Its definition of struct Array in the Swift module has it
• Nothing more generic/abstract?
- It adopts protocol CollectionType which ensures it as well
• Nothing more generic/abstract?
- It adopts protocol Indexable as well...
• Yup, sounds like the best we can do. Can we then extend it to have this feature we want?
- But we have very limited types (no Int) and properties (no count) to work with now!
• It will be enough. Swift's stdlib is done pretty well ;)
extension Indexable {
public subscript(safe safeIndex: Index) -> _Element? {
return safeIndex.distanceTo(endIndex) > 0 ? self[safeIndex] : nil
}
}
¹: not true, but it gives the idea

Because arrays may store nil values, it does not make sense to return a nil if an array[index] call is out of bounds.
Because we do not know how a user would like to handle out of bounds problems, it does not make sense to use custom operators.
In contrast, use traditional control flow for unwrapping objects and ensure type safety.
if let index = array.checkIndexForSafety(index:Int)
let item = array[safeIndex: index]
if let index = array.checkIndexForSafety(index:Int)
array[safeIndex: safeIndex] = myObject
extension Array {
#warn_unused_result public func checkIndexForSafety(index: Int) -> SafeIndex? {
if indices.contains(index) {
// wrap index number in object, so can ensure type safety
return SafeIndex(indexNumber: index)
} else {
return nil
}
}
subscript(index:SafeIndex) -> Element {
get {
return self[index.indexNumber]
}
set {
self[index.indexNumber] = newValue
}
}
// second version of same subscript, but with different method signature, allowing user to highlight using safe index
subscript(safeIndex index:SafeIndex) -> Element {
get {
return self[index.indexNumber]
}
set {
self[index.indexNumber] = newValue
}
}
}
public class SafeIndex {
var indexNumber:Int
init(indexNumber:Int){
self.indexNumber = indexNumber
}
}

I realize this is an old question. I'm using Swift5.1 at this point, the OP was for Swift 1 or 2?
I needed something like this today, but I didn't want to add a full scale extension for just the one place and wanted something more functional (more thread safe?). I also didn't need to protect against negative indices, just those that might be past the end of an array:
let fruit = ["Apple", "Banana", "Coconut"]
let a = fruit.dropFirst(2).first // -> "Coconut"
let b = fruit.dropFirst(0).first // -> "Apple"
let c = fruit.dropFirst(10).first // -> nil
For those arguing about Sequences with nil's, what do you do about the first and last properties that return nil for empty collections?
I liked this because I could just grab at existing stuff and use it to get the result I wanted. I also know that dropFirst(n) is not a whole collection copy, just a slice. And then the already existent behavior of first takes over for me.

I found safe array get, set, insert, remove very useful. I prefer to log and ignore the errors as all else soon gets hard to manage. Full code bellow
/**
Safe array get, set, insert and delete.
All action that would cause an error are ignored.
*/
extension Array {
/**
Removes element at index.
Action that would cause an error are ignored.
*/
mutating func remove(safeAt index: Index) {
guard index >= 0 && index < count else {
print("Index out of bounds while deleting item at index \(index) in \(self). This action is ignored.")
return
}
remove(at: index)
}
/**
Inserts element at index.
Action that would cause an error are ignored.
*/
mutating func insert(_ element: Element, safeAt index: Index) {
guard index >= 0 && index <= count else {
print("Index out of bounds while inserting item at index \(index) in \(self). This action is ignored")
return
}
insert(element, at: index)
}
/**
Safe get set subscript.
Action that would cause an error are ignored.
*/
subscript (safe index: Index) -> Element? {
get {
return indices.contains(index) ? self[index] : nil
}
set {
remove(safeAt: index)
if let element = newValue {
insert(element, safeAt: index)
}
}
}
}
Tests
import XCTest
class SafeArrayTest: XCTestCase {
func testRemove_Successful() {
var array = [1, 2, 3]
array.remove(safeAt: 1)
XCTAssert(array == [1, 3])
}
func testRemove_Failure() {
var array = [1, 2, 3]
array.remove(safeAt: 3)
XCTAssert(array == [1, 2, 3])
}
func testInsert_Successful() {
var array = [1, 2, 3]
array.insert(4, safeAt: 1)
XCTAssert(array == [1, 4, 2, 3])
}
func testInsert_Successful_AtEnd() {
var array = [1, 2, 3]
array.insert(4, safeAt: 3)
XCTAssert(array == [1, 2, 3, 4])
}
func testInsert_Failure() {
var array = [1, 2, 3]
array.insert(4, safeAt: 5)
XCTAssert(array == [1, 2, 3])
}
func testGet_Successful() {
var array = [1, 2, 3]
let element = array[safe: 1]
XCTAssert(element == 2)
}
func testGet_Failure() {
var array = [1, 2, 3]
let element = array[safe: 4]
XCTAssert(element == nil)
}
func testSet_Successful() {
var array = [1, 2, 3]
array[safe: 1] = 4
XCTAssert(array == [1, 4, 3])
}
func testSet_Successful_AtEnd() {
var array = [1, 2, 3]
array[safe: 3] = 4
XCTAssert(array == [1, 2, 3, 4])
}
func testSet_Failure() {
var array = [1, 2, 3]
array[safe: 4] = 4
XCTAssert(array == [1, 2, 3])
}
}

Swift 5.x
An extension on RandomAccessCollection means that this can also work for ArraySlice from a single implementation. We use startIndex and endIndex as array slices use the indexes from the underlying parent Array.
public extension RandomAccessCollection {
/// Returns the element at the specified index if it is within bounds, otherwise nil.
/// - complexity: O(1)
subscript (safe index: Index) -> Element? {
guard index >= startIndex, index < endIndex else {
return nil
}
return self[index]
}
}

extension Array {
subscript (safe index: UInt) -> Element? {
return Int(index) < count ? self[Int(index)] : nil
}
}
Using Above mention extension return nil if anytime index goes out of bound.
let fruits = ["apple","banana"]
print("result-\(fruits[safe : 2])")
result - nil

I have padded the array with nils in my use case:
let components = [1, 2]
var nilComponents = components.map { $0 as Int? }
nilComponents += [nil, nil, nil]
switch (nilComponents[0], nilComponents[1], nilComponents[2]) {
case (_, _, .Some(5)):
// process last component with 5
default:
break
}
Also check the subscript extension with safe: label by Erica Sadun / Mike Ash: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/

The "Commonly Rejected Changes" for Swift list contains a mention of changing Array subscript access to return an optional rather than crashing:
Make Array<T> subscript access return T? or T! instead of T: The current array behavior is intentional, as it accurately reflects the fact that out-of-bounds array access is a logic error. Changing the current behavior would slow Array accesses to an unacceptable degree. This topic has come up multiple times before but is very unlikely to be accepted.
https://github.com/apple/swift-evolution/blob/master/commonly_proposed.md#strings-characters-and-collection-types
So the basic subscript access will not be changing to return an optional.
However, the Swift team/community does seem open to adding a new optional-returning access pattern to Arrays, either via a function or subscript.
This has been proposed and discussed on the Swift Evolution forum here:
https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871
Notably, Chris Lattner gave the idea a "+1":
Agreed, the most frequently suggested spelling for this is: yourArray[safe: idx], which seems great to me. I am very +1 for adding this.
https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871/13
So this may be possible out of the box in some future version of Swift. I'd encourage anyone who wants it to contribute to that Swift Evolution thread.

Not sure why no one, has put up an extension that also has a setter to automatically grow the array
extension Array where Element: ExpressibleByNilLiteral {
public subscript(safe index: Int) -> Element? {
get {
guard index >= 0, index < endIndex else {
return nil
}
return self[index]
}
set(newValue) {
if index >= endIndex {
self.append(contentsOf: Array(repeating: nil, count: index - endIndex + 1))
}
self[index] = newValue ?? nil
}
}
}
Usage is easy and works as of Swift 5.1
var arr:[String?] = ["A","B","C"]
print(arr) // Output: [Optional("A"), Optional("B"), Optional("C")]
arr[safe:10] = "Z"
print(arr) // [Optional("A"), Optional("B"), Optional("C"), nil, nil, nil, nil, nil, nil, nil, Optional("Z")]
Note: You should understand the performance cost (both in time/space) when growing an array in swift - but for small problems sometimes you just need to get Swift to stop Swifting itself in the foot

To propagate why operations fail, errors are better than optionals.
public extension Collection {
/// Ensure an index is valid before accessing an element of the collection.
/// - Returns: The same as the unlabeled subscript, if an error is not thrown.
/// - Throws: `AnyCollection<Element>.IndexingError`
/// if `indices` does not contain `index`.
subscript(validating index: Index) -> Element {
get throws {
guard indices.contains(index)
else { throw AnyCollection<Element>.IndexingError() }
return self[index]
}
}
}
public extension AnyCollection {
/// Thrown when `[validating:]` is called with an invalid index.
struct IndexingError: Error { }
}
XCTAssertThrowsError(try ["🐾", "🥝"][validating: 2])
let collection = Array(1...10)
XCTAssertEqual(try collection[validating: 0], 1)
XCTAssertThrowsError(try collection[validating: collection.endIndex]) {
XCTAssert($0 is AnyCollection<Int>.IndexingError)
}

I think this is not a good idea. It seems preferable to build solid code that does not result in trying to apply out-of-bounds indexes.
Please consider that having such an error fail silently (as suggested by your code above) by returning nil is prone to producing even more complex, more intractable errors.
You could do your override in a similar fashion you used and just write the subscripts in your own way. Only drawback is that existing code will not be compatible. I think to find a hook to override the generic x[i] (also without a text preprocessor as in C) will be challenging.
The closest I can think of is
// compile error:
if theIndex < str.count && let existing = str[theIndex]
EDIT: This actually works. One-liner!!
func ifInBounds(array: [AnyObject], idx: Int) -> AnyObject? {
return idx < array.count ? array[idx] : nil
}
if let x: AnyObject = ifInBounds(swiftarray, 3) {
println(x)
}
else {
println("Out of bounds")
}

I have made a simple extension for array
extension Array where Iterator.Element : AnyObject {
func iof (_ i : Int ) -> Iterator.Element? {
if self.count > i {
return self[i] as Iterator.Element
}
else {
return nil
}
}
}
it works perfectly as designed
Example
if let firstElemntToLoad = roots.iof(0)?.children?.iof(0)?.cNode,

You can try
if index >= 0 && index < array.count {
print(array[index])
}

To be honest I faced this issue too. And from performance point of view a Swift array should be able to throw.
let x = try a[y]
This would be nice and understandable.

When you only need to get values from an array and you don't mind a small performance penalty (i.e. if your collection isn't huge), there is a Dictionary-based alternative that doesn't involve (a too generic, for my taste) collection extension:
// Assuming you have a collection named array:
let safeArray = Dictionary(uniqueKeysWithValues: zip(0..., array))
let value = safeArray[index] ?? defaultValue;

2022
infinite index access and safe idx access(returns nil in case no such idex):
public extension Collection {
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
subscript (infinityIdx idx: Index) -> Element where Index == Int {
return self[ abs(idx) % self.count ]
}
}
but be careful, it will throw an exception in case of array/collection is empty
usage
(0...10)[safe: 11] // nil
(0...10)[infinityIdx: 11] // 0
(0...10)[infinityIdx: 12] // 1
(0...10)[infinityIdx: 21] // 0
(0...10)[infinityIdx: 22] // 1

Swift 5 Usage
extension WKNavigationType {
var name : String {
get {
let names = ["linkAct","formSubm","backForw","reload","formRelo"]
return names.indices.contains(self.rawValue) ? names[self.rawValue] : "other"
}
}
}
ended up with but really wanted to do generally like
[<collection>][<index>] ?? <default>
but as the collection is contextual I guess it's proper.

Related

How to use firstIndex in Switft to find all results

I am trying to split a string into an array of letters, but keep some of the letters together. (I'm trying to break them into sound groups for pronunciation, for example).
So, for example, all the "sh' combinations would be one value in the array instead of two.
It is easy to find an 's' in an array that I know has an "sh" in it, using firstIndex. But how do I get more than just the first, or last, index of the array?
The Swift documentation includes this example:
let students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]
if let i = students.firstIndex(where: { $0.hasPrefix("A") }) {
print("\(students[i]) starts with 'A'!")
}
// Prints "Abena starts with 'A'!"
How do I get both Abena and Akosua (and others, if there were more?)
Here is my code that accomplishes some of what I want (please excuse the rather lame error catching)
let message = "she sells seashells"
var letterArray = message.map { String($0)}
var error = false
while error == false {
if message.contains("sh") {
guard let locate1 = letterArray.firstIndex(of: "s") else{
error = true
break }
let locate2 = locate1 + 1
//since it keeps finding an s it doesn't know how to move on to rest of string and we get an infinite loop
if letterArray[locate2] == "h"{
letterArray.insert("sh", at: locate1)
letterArray.remove (at: locate1 + 1)
letterArray.remove (at: locate2)}}
else { error = true }}
print (message, letterArray)
Instead of first use filter you will get both Abena and Akosua (and others, if there were more?)
extension Array where Element: Equatable {
func allIndexes(of element: Element) -> [Int] {
return self.enumerated().filter({ element == $0.element }).map({ $0.offset })
}
}
You can then call
letterArray.allIndexes(of: "s") // [0, 4, 8, 10, 13, 18]
You can filter the collection indices:
let students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]
let indices = students.indices.filter({students[$0].hasPrefix("A")})
print(indices) // "[1, 4]\n"
You can also create your own indices method that takes a predicate:
extension Collection {
func indices(where predicate: #escaping (Element) throws -> Bool) rethrows -> [Index] {
try indices.filter { try predicate(self[$0]) }
}
}
Usage:
let indices = students.indices { $0.hasPrefix("A") }
print(indices) // "[1, 4]\n"
or indices(of:) where the collection elements are Equatable:
extension Collection where Element: Equatable {
func indices(of element: Element) -> [Index] {
indices.filter { self[$0] == element }
}
}
usage:
let message = "she sells seashells"
let indices = message.indices(of: "s")
print(indices)
Note: If you need to find all ranges of a substring in a string you can check this post.
Have fun!
["Kofi", "Abena", "Peter", "Kweku", "Akosua"].forEach {
if $0.hasPrefix("A") {
print("\($0) starts with 'A'!")
}
}
If you really want to use the firstIndex method, here's a recursive(!) implementation just for fun :D
extension Collection where Element: Equatable {
/// Returns the indices of an element from the specified index to the end of the collection.
func indices(of element: Element, fromIndex: Index? = nil) -> [Index] {
let subsequence = suffix(from: fromIndex ?? startIndex)
if let elementIndex = subsequence.firstIndex(of: element) {
return [elementIndex] + indices(of: element, fromIndex: index(elementIndex, offsetBy: 1))
}
return []
}
}
Recursions
Given n instances of element in the collection, the function will be called n+1 times (including the first call).
Complexity
Looking at complexity, suffix(from:) is O(1), and firstIndex(of:) is O(n). Assuming that firstIndex terminates once it encounters the first match, any recursions simply pick up where we left off. Therefore, indices(of:fromIndex:) is O(n), just as good as using filter. Sadly, this function is not tail recursive... although we can change that by keeping a running total.
Performance
[Maybe I'll do this another time.]
Disclaimer
Recursion is fun and all, but you should probably use Leo Dabus' solution.

Exclude element in array when iterating using map

I have code like below
let myNums = getXYZ(nums: [1,2,3,4,5])
func getXYZ(nums: [Int]) -> [Int] {
let newNum = nums.map { (num) -> Int in
if num == 2 {
//do something and continue execution with next element in list like break/fallthrough
return 0
}
return num
}
return newNum
}
print(myNums)`
This prints [1,0,3,4,5]
but i want the output to be [1,3,4,5]. How can I exclude 2? I want to alter the if statement used so as to not include in array when it sees number 2
I have to use .map here but to exclude 2..is there any possibility
Please let me know
I'd simply do a filter as described as your problem, you want to filter the numbers by removing another number.
var myNums = [1, 2, 3, 4, 5]
let excludeNums = [2]
let newNum = myNums.filter({ !excludeNums.contains($0) })
print(newNum) //1, 3, 4, 5
If you need to do a map, you could do a map first then filter.
let newNum = myNums.map({ $0*2 }).filter({ !excludeNums.contains($0) })
print(newNum) //4, 6, 8, 10
This maps to multiplying both by 2 and then filtering by removing the new 2 from the list. If you wanted to remove the initial 2 you would have to filter first then map. Since both return a [Int] you can call the operations in any order, as you deem necessary.
As suggested by #koropok, I had to make below changes
nums.compactMap { (num) -> Int? in
....
if num == 2 {
return nil
}
I suggest you to use filter instead of map:
let myNums = [1,2,3,4,5]
let result1 = myNums.filter{ return $0 != 2 }
print(result1) // This will print [1,3,4,5]
If you must definitely use map, then use compactMap:
let result2 = myNums.compactMap { return $0 == 2 ? nil : $0 }
print(result2) // This will print [1,3,4,5]
Hope this helps
filter is more appropriate than map for your use case.
If you want to exclude only 1 number:
func getXYZ(nums: [Int]) -> [Int] {
return nums.filter { $0 != 2 }
}
If you want to exclude a list of numbers, store those exclusions in a Set since Set.contains runs in O(1) time, whereas Array.contains runs in O(n) time.
func getXYZ(nums: [Int]) -> [Int] {
let excluded: Set<Int> = [2,4]
return nums.filter { !excluded.contains($0) }
}
My solution is based on enumerated() method:
let elements = nums.enumerated().compactMap { (index, value) in
( index == 0 ) ? nil : value
}
enumerated() add element's index as first closure argument

Swift - Determine if Array1 contains at least one object from Array2

I have 2 Arrays. Say, array1 = [1,2,3,4,5] and array2 = [2,3]. How could I check in swift if array1 contains at least one item from array2?
You can do this by simply passing in your array2's contains function into your array1's contains function (or vice versa), as your elements are Equatable.
let array1 = [2, 3, 4, 5]
let array2 = [20, 15, 2, 7]
// this is just shorthand for array1.contains(where: { array2.contains($0) })
if array1.contains(where: array2.contains) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works by looping through array 1's elements. For each element, it will then loop through array 2 to check if it exists in that array. If it finds that element, it will break and return true – else false.
This works because there are actually two flavours of contains. One takes a closure in order to check each element against a custom predicate, and the other just compares an element directly. In this example, array1 is using the closure version, and array2 is using the element version. And that is the reason you can pass a contains function into another contains function.
Although, as correctly pointed out by #AMomchilov, the above algorithm is O(n2). A good set intersection algorithm is O(n), as element lookup is O(1). Therefore if your code is performance critical, you should definitely use sets to do this (if your elements are Hashable), as shown by #simpleBob.
Although if you want to take advantage of the early exit that contains gives you, you'll want to do something like this:
extension Sequence where Iterator.Element : Hashable {
func intersects<S : Sequence>(with sequence: S) -> Bool
where S.Iterator.Element == Iterator.Element
{
let sequenceSet = Set(sequence)
return self.contains(where: sequenceSet.contains)
}
}
if array1.intersects(with: array2) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works much the same as the using the array's contains method – with the significant difference of the fact that the arraySet.contains method is now O(1). Therefore the entire method will now run at O(n) (where n is the greater length of the two sequences), with the possibility of exiting early.
With Swift 5, you can use one of the following paths in order to find if two arrays have common elements or not.
#1. Using Set isDisjoint(with:) method
Set has a method called isDisjoint(with:). isDisjoint(with:) has the following declaration:
func isDisjoint(with other: Set<Element>) -> Bool
Returns a Boolean value that indicates whether the set has no members in common with the given sequence.
In order to test if two arrays have no common elements, you can use the Playground sample code below that implements isDisjoint(with:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [50, 100, 200]
let hasNoCommonElement = Set(array1).isDisjoint(with: array2)
print(hasNoCommonElement) // prints: true
#2. Using Set intersection(_:) method
Set has a method called intersection(_:). intersection(_:) has the following declaration:
func intersection<S>(_ other: S) -> Set<Element> where Element == S.Element, S : Sequence
Returns a new set with the elements that are common to both this set and the given sequence.
In order to test if two arrays have no common elements or one or more common elements, you can use the Playground sample code below that implements intersection(_:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [2, 3, 18]
let intersection = Set(array1).intersection(array2)
print(intersection) // prints: [18, 3]
let hasCommonElement = !intersection.isEmpty
print(hasCommonElement) // prints: true
An alternative way would be using Sets:
let array1 = [1,2,3,4,5]
let array2 = [2,3]
let set1 = Set(array1)
let intersect = set1.intersect(array2)
if !intersect.isEmpty {
// do something with the intersecting elements
}
Swift 5
Just make an extension
public extension Sequence where Element: Equatable {
func contains(anyOf sequence: [Element]) -> Bool {
return self.filter { sequence.contains($0) }.count > 0
}
}
Use:
let someArray = ["one", "two", "three"]
let string = "onE, Cat, dog"
let intersects = string
.lowercased()
.replacingOccurrences(of: " ", with: "")
.components(separatedBy: ",")
.contains(anyOf: someArray)
print(intersects) // true
let a1 = [1, 2, 3]
let a2 = [2, 3, 4]
Option 1
a2.filter { a1.contains($0) }.count > 1
Option 2
a2.reduce(false, combine: { $0 || a1.contains($1) })
Hope this helps.
//
// Array+CommonElements.swift
//
import Foundation
public extension Array where Element: Hashable {
func set() -> Set<Array.Element> {
return Set(self)
}
func isSubset(of array: Array) -> Bool {
self.set().isSubset(of: array.set())
}
func isSuperset(of array: Array) -> Bool {
self.set().isSuperset(of: array.set())
}
func commonElements(between array: Array) -> Array {
let intersection = self.set().intersection(array.set())
return intersection.map({ $0 })
}
func hasCommonElements(with array: Array) -> Bool {
return self.commonElements(between: array).count >= 1 ? true : false
}
}

Best way to check if object is out of bounds in array

What is the best practice to check if an object exists (is within bounds) at a specific index in an Array?
It would be nice to have it as simple as this, but that is unfortunately not possible:
let testArray = ["A", "B", "C", "D"]
if let result = testArray[6] {
println("Result: \(result)")
}
else {
println("Result does not exist. Out of bounds.")
}
Will I have to check against the total count?
Thank you!
You could also make an extension to Array, so you will be able to check with if-let:
extension Array {
func at(index: Int) -> Element? {
if index < 0 || index > self.count - 1 {
return nil
}
return self[index]
}
}
let arr = [1, 2, 3]
if let value = arr.at(index: 2) {
print(value)
}
You can use the ~= operator in conjunction with the indices function, which is a shortcut for creating a range of the full index range of a container:
let a = [1,2,3]
let idx = 3 // one past the end
if indices(a) ~= idx {
println("within")
}
else {
println("without")
}
The one thing to note is this works with any kind of container that has a comparable index, not just ones like array that have integer indices. Thinking of indices as numbers is generally a good habit to get out of, since it helps you think more generally about algorithms on containers without these indices, such as strings or dictionaries:
let s = "abc"
let idx = s.endIndex
idx < count(s) // this won't compile
idx < s.startIndex // but this will
// and so will this
if indices(s) ~= idx {
println("within")
}
else {
println("without")
}
The more general your algorithms, the more chance you will be able to factor them out into generics and increase re-use.
#alkku has it but for simplicity and using the most underused syntactic form in all languages ?::
extension Array {
func atIndex(index: Int) -> T? {
return (0 <= index && index < self.count
? self[index]
: nil)
}
}
There is a new (neat) way to do it in Swift:
array.indices.contains(index)
Check if object index used should be greater or equal to zero and should be less total count of array like this :
//Here index is object you want for
if(index >= 0 && index < [yourArray count])
{
//inside of array
}
else
{
//out of bound
}
To check whether or not an index is inside the bounds of an array in swift you would use
if index < testArray.count {
//index is inside bounds
} else {
//index is outside bounds
}

Implementing functions in swift

I am new to swift and trying to implement a simple function that takes minimum and max number as input and returns an array with all the numbers in the limit. I am getting an error
//Error: Reference to generic type 'Array' requires arguments in <...>
may I know what I am missing on?
func serialNumberLimits(minimumNumber n1:Int, maximumNumber n2:Int) -> Array {
// Initialized an empty array
var array = Int[]()
//Initialized a "Temp" variable
var temp:Int = 0
for index in n1..n2 {
temp += n1
n1++
if index == 1 { array.insert(temp, atIndex: 0) }
else { array.insert(temp, atIndex: index-1) }
}
return array
}
Use following function
1)As you are using n1 in function and changing its value so declare it as var as all parameters are constants in swift by default
2)Use Array<Int> as it needs to be define which type of array is in swift.Swift is strongly typed language so all type need to be defined.
Run following code it will compile with no errors
func serialNumberLimits(var minimumNumber n1:Int, maximumNumber n2:Int) -> Array<Int> {
// Initialized an empty array
var array = Int[]()
//Initialized a "Temp" variable
var temp:Int = 0
for index in n1..n2 {
temp += n1
n1++
if index == 1 { array.insert(temp, atIndex: 0) }
else { array.insert(temp, atIndex: index) }
}
return array
}

Resources