Error comparing Int array slice elements in Swift 4 Xcode 9 - arrays

In my playground in Xcode 9
func somefn<Int>(_ a:[Int])->[Int]{
if(a.count == 1 || a.isEmpty){
return a
}else{
let e1 = (a.count/2) - 1
let s2 = e1 + 1
let lhs = Array(a[...e1])
let rhs = Array(a[s2...])
if lhs.first! > rhs.first! {
print("LHS first is bigger than RHS second")
}
return []
}
}
let s = [1,4,6,9]
somefn(s)
Gives error:
Binary operator '>' cannot be applied to two 'Int' operands

The issue seems to be that the <Int> syntax after the function name is being treated as a generic type placeholder and not the actual data type of Int.
If you replace <Int> with T in the whole function declaration you still get the error.
func somefn<T>(_ a:[T]) -> [T] {
This makes sense since the < operator is now ambiguous. By updating the signature to indicate that T must be a Comparable, the problem goes away:
func somefn<T:Comparable>(_ a:[T]) -> [T] {
You could also do:
func somefn<Int:Comparable>(_ a:[Int]) -> [Int] {
but that's confusing to any reader since that Int is not the data type of Int.
This all assumes you intend to be able to pass arrays of different data types and get back an array of the same type.
If you just want to support an array of Int for both the parameter and return type, change the signature to:
func somefn(_ a:[Int]) -> [Int] {

Related

"ambiguous subscript" when trying to return an array

I'm looking to extract sub-arrays in Swift 4 and running into unexpected difficulties.
The following trivial cases work as expected:
let array1 = ["a", "b", "c","x","y","z"]
array1[2...3] // returns ["c","x"], as it should
as does the following variation:
let m=2
let n=3
array1[m...n] // returns ["c","x"], as it should
But if I try to create a function around this, I get an error message ambiguous subscript with base type 'Array<Any>' and index type 'CountableClosedRange<Int>':
func arrayTake(arrayIn: Array<Any>) -> Array<Any> {
return arrayIn[2...3]
} // no good
And of course, the following form, which is closer to what I actually want to do, fails with the same error message:
func arrayTake(m: Int, n: Int, arrayIn: Array<Any>) -> Array<Any> {
return arrayIn[m...n]
} // no good
I have seen other questions here about ambiguous subscripts but have not been able to translate the answers there into a solution to my problem. Any guidance would be appreciated.
You are getting such error because you are trying to return an ArraySlice instead of the expected Array<Any>:
Slices Are Views onto Arrays
You may want to convert such slice using a dedicated Array constructor:
func arrayTake(m: Int, n: Int, arrayIn: Array<Any>) -> Array<Any> {
return Array<Any>(arrayIn[m...n])
}
Moreover, please note that long-term storage of ArraySlice is discouraged, a slice holds a reference to the entire storage of a larger array, not just to the portion it presents, even after the original array’s lifetime ends. Long-term storage of a slice may therefore prolong the lifetime of elements that are no longer otherwise accessible, which can appear to be memory and object leakage.
Problem is Datatype mismatch.
array1[2...3] This statement does not return Array it returns ArraySlice a special DataType (struct) for this kind of array operation.
So you can solve this by two way.
Change return type to ArraySlice
func arrayTake(m: Int, n: Int, arrayIn: Array<Any>) -> ArraySlice<Any>
{
return arrayIn[m...n]
}
OR
Convert ArraySlice to Array
func arrayTake(m: Int, n: Int, arrayIn: Array<Any>) -> Array<Any> {
return Array(arrayIn[m...n])
}
Both will print same output but keep in mind that they are not same:
let array1 = ["a", "b", "c","x","y","z"]
let result = arrayTake(m: 2, n: 3, arrayIn: array1)
print(result) //["c","x"], as it should
Check this for more detail: https://medium.com/#marcosantadev/arrayslice-in-swift-4e18522ab4e4

Cannot subscript a value of type Array<Element> with index of type CountableRange<Int>

Error:
Cannot subscript a value of type Array with index of type CountableRange
I want to find say some top elements from any array/Sequence.
extension Array {
func top(max:Int) -> Array {
guard self.count > max else {
return self
}
let last = max - 1
return self[0..<max]
}
}
This functionality already exists in the standard library, in the form of Array.prefix(_:)
But to address the issue in this code:
The error here is actually not actually that you can't subscript a value of type Array with index of type CountableRange, but rather, that you can't do so to produce a result of type Array.
Subscripting an Array by a range yields an ArraySlice, not an Array. Thus, your code would have to be:
extension Array {
func first(_ maxLength: Int) -> ArraySlice<Element> {
return self[0 ..< Swift.min(maxLength, self.count)]
}
}
Thanks #Leo and #Alexander for your prompt replies :)
extension Array {
func first(max: Int) -> Array {
return Array(prefix(max))
}
}

Swift Error: Cannot convert value of type 'ArraySlice' to expected argument type

I'm getting this error and am new to Swift. I want to take the last 5 points of an array >= 5, and pass those 5 points as an array argument to a function. How can I achieve this and get past this error?
Cannot convert value of type 'ArraySlice' to expected argument type '[CGPoint]'
if (self.points?.count >= 5) {
let lastFivePoints = self.points![(self.points!.count-5)..<self.points!.count]
let angle = VectorCalculator.angleWithArrayOfPoints(lastFivePoints)
}
You need to convert ArraySlice to Array using method Array(Slice<Type>)
if (self.points?.count >= 5) {
let lastFivePoints = Array(self.points![(self.points!.count-5)..<self.points!.count])
let angle = VectorCalculator.angleWithArrayOfPoints(lastFivePoints)
}
Instead of the range operator you can use prefix(upTo end: Self.Index) method that return ArraySlice which makes your code shorter. Method's definition: The method returns a subsequence from the start of the collection up to, but not including, the specified position (index).
if (self.points?.count >= 5) {
let lastFivePoints = Array<CGPoint>(self.points?.prefix(upTo:5)) as [AnyObject]
let angle = VectorCalculator.angleWithArrayOfPoints(lastFivePoints)
}
// You can also do this
let lastFivePoints = Array<CGPoint>(self.points?[0...4])
I tried using Array(lastFivePoints) but i got error
Type of expression is ambiguous without more context
I ended up doing:
let arr = lastFivePoints.map({ (x) -> T in
return x
})
where T is the content class CGPoint for this example

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

Resources