Swift indexOf inconsistent behavior [duplicate] - arrays

This question already has answers here:
NSObject subclass in Swift: hash vs hashValue, isEqual vs ==
(4 answers)
Closed 6 years ago.
It seems that I've facing inconsistent behavior with Swift's indexOf function.
There are two indexOf functions in Swift:
1. The first one takes Equatable as an argument: array.indexOf(obj)
2. The second takes matching closure as an argument: array.indexOf{$0 == obj}
I have an NSObject subclass for which == operator is defined, i.e. it conforms to Equatable, therefore, I assume both function to work exactly the same (with closure in example).
However, the 1st method behaves inconsistently, in particular it returns nil when being invoked with object, instance of which is not present in array.
To illustrate issue, I provide example code with comments:
class EqObj: NSObject {
let value: Int
init(value: Int) {
self.value = value
}
}
func ==(lhs: EqObj, rhs: EqObj) -> Bool{
return lhs.value == rhs.value
}
var array = [Obj(value: 1), Obj(value: 3), Obj(value: 5)]
var object = Obj(value: 5)
// returns nil, should return 2 - incorrect
array.indexOf(object) // Instance is not present
// returns 2, correct
array.indexOf(array.last!) // Instance is present
// returns 2, correct
array.indexOf{$0 == object} // Instance is not present, predicate
// returns non-empty array, correct
array.filter{$0 == object} // Instance is not present, predicate
The issue is reproducible only with NSObject subclasses. When I change Obj: NSObject to Obj: Equatable method indexOf() works exactly as expected, i.e. returns 2.
The question is whether this can be considered a bug?
My assumption is that array.indexOf(object) invokes isEqual method of NSObject and not my overloaded == operator.
My solution:
I use array.indexOf{$0 == object}

First of all with this definition I am getting different results
class Obj: NSObject {
let value: Int
init(value:Int) {
self.value = value
}
}
func ==(lhs: Obj, rhs: Obj) -> Bool{
return lhs.value == rhs.value
}
var array = [Obj(value: 1), Obj(value: 3), Obj(value: 5)]
var object = Obj(value: 5)
here they are
You can read below the reasons after each result
Result #1
array.indexOf(object) // nil
In this case the isEqual method is used, so you are just comparing memory address. And since the object var references an object NOT inside the array, you get nil as result
Result #2
array.indexOf(array.last!) // 2
Again isEqual is used. However here you are passing to indexOf the reference to the last object in the array so it works.
Result #3
array.indexOf { $0 == object } // 2
Here you are explicitly using the == operator you defined so the value property is used.
Result #4
array.filter { $0 == object } // [{NSObject, value 5}]
Again the == operator is used and then the value property is used to check for equality.

Related

2D Array extension Swift 3.1.1

I am trying to make an Array extension in Swift 3.1.1 that supports the addition of an object to a certain index in a 2D Array even if the array hasn't been populated yet. The extension should also provide the ability to get an object at certain indexPath. I have the code for this in Swift 2 but I don't seem to be able to migrate it to Swift 3. This is the Swift 2 code:
extension Array where Element: _ArrayProtocol, Element.Iterator.Element: Any {
mutating func addObject(_ anObject : Element.Iterator.Element, toSubarrayAtIndex idx : Int) {
while self.count <= idx {
let newSubArray = Element()
self.append(newSubArray)
}
var subArray = self[idx]
subArray.append(anObject)
}
func objectAtIndexPath(_ indexPath: IndexPath) -> Any {
let subArray = self[indexPath.section]
return subArray[indexPath.row] as Element.Iterator.Element
}
}
The code is taken from this answer.
As Martin says in his answer here, _ArrayProtocol is no longer public in Swift 3.1, therefore meaning that you cannot use it as a constraint in your extension.
A simple alternative in your case is to instead constrain the Array's Element to being a RangeReplaceableCollection – which both defines an init() requirement meaning "empty collection", and an append(_:) method in order to add elements to the collection.
extension Array where Element : RangeReplaceableCollection {
typealias InnerCollection = Element
typealias InnerElement = InnerCollection.Iterator.Element
mutating func fillingAppend(
_ newElement: InnerElement,
toSubCollectionAtIndex index: Index) {
if index >= count {
append(contentsOf: repeatElement(InnerCollection(), count: index + 1 - count))
}
self[index].append(newElement)
}
}
Note also that we're doing the append as a single call (using append(contentsOf:), ensuring that we only have to resize the outer array at most once.
For your method to get an element from a given IndexPath, you can just constrain the inner element type to being a Collection with an Int Index:
// could also make this an extension on Collection where the outer Index is also an Int.
extension Array where Element : Collection, Element.Index == Int {
subscript(indexPath indexPath: IndexPath) -> Element.Iterator.Element {
return self[indexPath.section][indexPath.row]
}
}
Note that I've made it a subscript rather than a method, as I feel it fits better with Array's API.
You can now simply use these extensions like so:
var arr = [[Int]]()
arr.fillingAppend(6, toSubCollectionAtIndex: 3)
print(arr) // [[], [], [], [6]]
let indexPath = IndexPath(row: 0, section: 3)
print(arr[indexPath: indexPath]) // 6
Although of course if you know the size of the outer array in advance, the fillingAppend(_:toSubCollectionAtIndex:) method is redundant, as you can just create your nested array by saying:
var arr = [[Int]](repeating: [], count: 5)
which will create an [[Int]] array containing 5 empty [Int] elements.
There's no need to limit all these ideas to the concrete Array type.
Here's my solution. This discussion was great in that I just learned about RangeReplaceableCollection. Merging (what I think is) the best of both worlds, I pushed all the operations down (up?) the Type hierarchy as far as possible.
Subscript works on much more than Array as #Hamish says. But also, there's no need to constrain the index type, so we have to get rid of IndexPath. We can always sugar this with typealias Index2d = ...
extension Collection where Self.Element: Collection {
subscript(_ indexTuple: (row: Self.Index, column: Self.Element.Index)) -> Self.Element.Element {
get {
return self[indexTuple.row][indexTuple.column]
}
}
}
Why not have a mutable version at the most generic possible level (between Collection and RangeReplaceableCollection) (unfortunately I don't think the getter can be inherited when we redefine subscript):
extension MutableCollection where Self.Element: MutableCollection {
subscript(_ indexTuple: (row: Self.Index, column: Self.Element.Index)) -> Self.Element.Element {
get {
return self[indexTuple.row][indexTuple.column]
}
set {
self[indexTuple.row][indexTuple.column] = newValue
}
}
}
Then, if you want to initialize lazily, avoid using init:repeatedValue and revise set to have auto-initialization semantics. You can trap bounds overflow and add missing empty elements in both dimensions by integrating the accepted answer's fillingAppend idea.
And when creating a 2D initializer, why not extend the idea of repeating in the natural way:
extension RangeReplaceableCollection where Element: RangeReplaceableCollection {
init(repeating repeatedVal: Element.Element, extents: (row: Int, column: Int)) {
let repeatingColumn = Element(repeating: repeatedVal, count: extents.column)
self.init(repeating: repeatingColumn, count: extents.row)
}
}
Example Usage:
enum Player {
case first
case second
}
class Model {
let playerGrid: Array<Array<Player>> = {
var p = [[Player]](repeating: .first, extents: (row: 10, column: 10))
p[(3, 4)] = .second
print("Player at 3, 4 is: \(p[(row: 3, column: 4)])")
return p
}()
}

Swift: reference to array element by key

I have written my own small function to find an element in an array using a key. But I'm sure there is a ready to use implementation in Swift to get it in one line. Any hint?
func objectAtKey(array: [T], key: String) -> T? {
for element in array {
if element.name == key {
return element
}
}
return nil
}
I also know function indexOf, but this return an index, I have to use for further access. I think this is slower:
let index = array.indexOf({$0.name == key})
In Swift 3 (Xcode 8, currently beta 6) you can do
if let el = array.first(where: { $0.name == key }) {
// `el` is the first array element satisfying the condition.
// ...
} else {
// No array element satisfies the condition.
}
using the first(where:) method of the Sequence protocol:
/// Returns the first element of the sequence that satisfies the given
/// predicate or nil if no such element is found.
///
/// - Parameter predicate: A closure that takes an element of the
/// sequence as its argument and returns a Boolean value indicating
/// whether the element is a match.
/// - Returns: The first match or `nil` if there was no match.
public func first(where predicate: (Element) throws -> Bool) rethrows -> Element?
I think the best solution for you here is to use the indexOf with a Predicate that you have written. I would have written it like this though:
let array = ["Foo", "Bar", "Test"]
if let i = array.indexOf({$0 == "Foo"}) {
print(array[i])
}
To handle if the value does not exists if you need that.
Try this:
let element = array.filter{ $0.name == key }.first

Error Swift Programming How to search an Array of [Any]--> Generic parameter 'C.Generator.Element' cannot be bound to non-#ob

Hi I'm trying to search an array with various objects though I'm getting an error.
Generic parameter 'C.Generator.Element' cannot be bound to non-#ob
Here is my code I'm using:
var arraySearching = [Any]()
arraySearching = ["this","that",2]
find(arraySearching, 2)
How do you search arrays of type [Any]?
This is a misleading error message, but the problem is that find requires that the contents of the collection you are searching be Equatable, and Any isn’t. You can’t do much with Any at all in fact, never mind equate it with values of other types. You have to cast it to a real type and then use that.
That’d be easy to do in a closure, except there isn’t a version of find that takes a closure. But it’s easy to write one:
func find<C: CollectionType>(source: C, match: C.Generator.Element -> Bool) -> C.Index? {
for idx in indices(source) {
if match(source[idx]) { return idx }
}
return nil
}
Once you have this, you can use it to search for a value of a specific type in an array of Any:
find(arraySearching) {
// cast the candidate to an Int, then compare (comparing an
// optional result of as? works fine, nil == 2 is false)
($0 as? Int) == 2
}

Compare arrays in swift

Trying to understand how swift compares arrays.
var myArray1 : [String] = ["1","2","3","4","5"]
var myArray2 : [String] = ["1","2","3","4","5"]
// 1) Comparing 2 simple arrays
if(myArray1 == myArray2) {
println("Equality")
} else {
println("Equality no")
}
// -> prints equality -> thanks god
// 2) comparing to a "copy" of an array
// swift copies arrays when passed as parameters (as per doc)
func arrayTest(anArray: [String]) -> Bool {
return anArray == myArray1
}
println("Array test 1 is \(arrayTest(myArray1))")
println("Array test 2 is \(arrayTest(myArray2))")
// equality works for both
myArray2.append("test")
println("Array test 2 is \(arrayTest(myArray2))")
// false (obviously)
myArray2.removeAtIndex(5)
println("Array test 2 is \(arrayTest(myArray2))")
// true
Apple says there are optimisations behind the scene on array copies. Looks like sometimes - not always - structures are actually copied or not.
That said,
1) is == iterating over all the array to perform a element-based comparison ? (looks like it)
-> How about performance / memory usage on very large arrays then ?
2) Are we sure == will ever return true if all elements are equal ? I have bad memories of == on Java Strings
3) Is there a way to check if myArray1 and myArray2 are technically using the same "memory location" / pointer / etc. ? i'm after understanding how the optimisation works and potential caveats.
Thanks.
You’re right to be slightly nervous about ==:
struct NeverEqual: Equatable { }
func ==(lhs: NeverEqual, rhs: NeverEqual)->Bool { return false }
let x = [NeverEqual()]
var y = x
x == y // this returns true
[NeverEqual()] == [NeverEqual()] // false
x == [NeverEqual()] // false
let z = [NeverEqual()]
x == z // false
x == y // true
y[0] = NeverEqual()
x == y // now false
Why? Swift arrays do not conform to Equatable, but they do have an == operator, defined in the standard library as:
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool
This operator loops over the elements in lhs and rhs, comparing the values at each position. It does not do a bitwise compare – it calls the == operator on each pair of elements. That means if you write a custom == for your element, it’ll get called.
But it contains an optimization – if the underlying buffers for the two arrays are the same, it doesn’t bother, it just returns true (they contain identical elements, of course they’re equal!).
This issue is entirely the fault of the NeverEqual equality operator. Equality should be transitive, symmetric and reflexive, and this one isn't reflexive (x == x is false). But it could still catch you unawares.
Swift arrays are copy-on-write – so when you write var x = y it doesn’t actually make a copy of the array, it just points x’s storage buffer pointer at y’s. Only if x or y are mutated later does it then make a copy of the buffer, so that the unchanged variable is unaffected. This is critical for arrays to behave like value types but still be performant.
In early versions of Swift, you actually could call === on arrays (also in early versions, the mutating behaviour was a bit different, if you mutated x, y would also change even though it had been declared with let – which freaked people out so they changed it).
You can kinda reproduce the old behaviour of === on arrays with this (very implementation-dependent not to be relied-on except for poking and prodding investigations) trick:
let a = [1,2,3]
var b = a
a.withUnsafeBufferPointer { outer in
b.withUnsafeBufferPointer { inner in
println(inner.baseAddress == outer.baseAddress)
}
}
== in Swift is the same as Java's equals(), it compares values.
=== in Swift is the same as Java's ==, it compares references.
In Swift you can compare array content values as easy as this:
["1", "2"] == ["1", "2"]
But this will not work if you want to compare references:
var myArray1 = [NSString(string: "1")]
var myArray2 = [NSString(string: "1")]
myArray1[0] === myArray2[0] // false
myArray1[0] == myArray2[0] // true
So the answers:
I think the performance is optimal for doing value (not reference)
comparisons
Yes, if you want to compare values
Swift arrays are value type and not reference type. So the memory
location is the same only if you compare it to itself (or use unsafe
pointers)
It depends on how do you want to compare. For example:
["1", "2"] == ["1", "2"] // true
but
["1", "2"] == ["2", "1"] // false
If you need that second case to also be true and are ok with ignoring repetitive values, you can do:
Set(["1", "2"]) == Set(["2", "1"]) // true
(use NSSet for Swift 2)
For compare arrays of custom objects we can use elementsEqual.
class Person {
let ID: Int!
let name: String!
init(ID: Int, name: String) {
self.ID = ID
self.name = name
}
}
let oldFolks = [Person(ID: 1, name: "Ann"), Person(ID: 2, name: "Tony")]
let newFolks = [Person(ID: 2, name: "Tony"), Person(ID: 4, name: "Alice")]
if oldFolks.elementsEqual(newFolks, by: { $0.ID == $1.ID }) {
print("Same people in same order")
} else {
print("Nope")
}
Arrays conform to Equatable in Swift 4.1, negating the caveats mentioned in previous answers. This is available in Xcode 9.3.
https://swift.org/blog/conditional-conformance/
But just because they implemented == did not mean Array or Optional conformed to Equatable. Since these types can store non-equatable types, we needed to be able to express that they are equatable only when storing an equatable type.
This meant these == operators had a big limitation: they couldn’t be used two levels deep.
With conditional conformance, we can now fix this. It allows us to write that these types conform to Equatable—using the already-defined == operator—if the types they are based on are equatable.
If you have an array of custom objects, one has to be careful with the equality test, at least with Swift 4.1:
If the custom object is not a subclass of NSObject, the comparison uses the static func == (lhs: Nsobject, rhs: Nsobject) -> Bool, which has to be defined.
If it is a subclass of NSObject, it uses the func isEqual(_ object: Any?) -> Bool, which has to be overridden.
Please check the following code, and set breakpoints at all return statements.
class Object: Equatable {
static func == (lhs: Object, rhs: Object) -> Bool {
return true
}
}
The following class inheritates Equatable from NSObject
class Nsobject: NSObject {
static func == (lhs: Nsobject, rhs: Nsobject) -> Bool {
return true
}
override func isEqual(_ object: Any?) -> Bool {
return true
}
}
They can be compared with:
let nsObject1 = Nsobject()
let nsObject2 = Nsobject()
let nsObjectArray1 = [nsObject1]
let nsObjectArray2 = [nsObject2]
let _ = nsObjectArray1 == nsObjectArray2
let object1 = Object()
let object2 = Object()
let objectArray1 = [object1]
let objectArray2 = [object2]
let _ = objectArray1 == objectArray2
Whereas comparing arrays in swift, must be clear what are the comparison entities.
Ex:
A = [1,2,3]
B = [2,1,3]
return A == B -> false
The comparison is being made traversing both arrays and comparing its index's data.
return Set(A) == Set(B) -> true
Because of function Set(), the comparison is made by whole single value collection.
"Set operations are not limited to use with other sets. Instead, you
can perform set operations with another set, an array, or any other
sequence type."
Use the “equal to” operator (==) to test whether two sets contain > the same elements.
https://developer.apple.com/documentation/swift/set
Can find more here:
https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html

Swift: optional array count

In Objective-C, if I had the following property:
#property (strong, nonatomic) NSArray * myArray;
A method to return a number of objects in myArray would look like:
- (NSInteger) numberOfObjectsInMyArray
{
return [self.myArray count];
}
This would return either the number of objects in the array, or 0 if myArray == nil;
The best equivalent I can think of for doing this in Swift is:
var myArray: Array<String>?
func numberOfObjectsInMyArray() -> Int
{
return myArray ? myArray!.count : 0
}
So checking the optional array contains a value, and if so unwrap the array and return that value, otherwise return 0.
Is this the correct way to do this? Or is there something simpler?
Try using the nil coalescing operator.
According to the Apple Documentation:
The nil coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil.
So your function could look like this:
func numberOfObjectsInMyArray() -> Int {
return (myArray?.count ?? 0)
}
I agree with others that this could be a bad idea for a number of reasons (like making it look like there is an array with a count of "0" when there isn't actually an array at all) but hey, even bad ideas need an implementation.
EDIT:
So I'm adding this because two minutes after I posted this answer, I came across a reason for doing exactly what the author wants to do.
I am implementing the NSOutlineViewDataSource protocol in Swift. One of the functions required by the protocol is:
optional func outlineView(_ outlineView: NSOutlineView,
numberOfChildrenOfItem item: AnyObject?) -> Int
That function requires that you return the number of children of the item parameter. In my code, if the item has any children, they will be stored in an array, var children: [Person]?
I don't initialize that array until I actually add a child to the array.
In other words, at the time that I am providing data to the NSOutlineView, children could be nil or it could be populated, or it could have once been populated but subsequently had all objects removed from it, in which case it won't be nil but it's count will be 0. NSOutlineView doesn't care if children is nil - all it wants to know is how many rows it will need to display the item's children.
So, it makes perfect sense in this situation to return 0 if children is nil. The only reason for calling the function is to determine how many rows NSOutlineView will need. It doesn't care whether the answer is 0 because children is nil or because it is empty.
return (children?.count ?? 0) will do what I need. If children is nil it will return 0. Otherwise it will return count. Perfect!
That looks like the simpler way.
The Objective-C code is shorter only because nil is also a form of 0, being a C-based language.
Since swift is strongly typed you don't have such a shorthand. In this specific case it requires a little more effort, but in general it saves you most of the headaches caused by loose typing.
Concerning the specific case, is there a reason for making the array optional in the first place? You could just have an empty array. Something like this might work for you:
var myArray: Array<String> = []
func numberOfObjectsInMyArray() -> Int {
return myArray.count
}
(Source for this information)
How about using optional for return value?
var myArray: Array<String>?
func numberOfObjectsInMyArray() -> Int? {
return myArray?.count
}
I think that this way is safer.
(Source for this information)

Resources