I was writing some code from the Apple forum. Everything seems right but I keep getting two errors. Someone please help. The code is below followed by the errors.
protocol Container{
associatedtype ItemType
mutating func append(item: ItemType)
var count:Int{get}
subscript(i:Int)->ItemType{get}
}
extension Array: Container {}
func checkToSeeIfItemsEqual<C1:Container, C2:Container>(container1:C1, container2:C2) -> Bool where C1.ItemType == C2.ItemType, C1.ItemType:Equatable{
if container1.count != container2.count{
return false
}
for i in 0..<container1.count{
if container1[i] != container2[i]{
return false
}
}
return true
}
var damnArray = [1, 2, 4]
var damnArray2 = [1, 2, 4]
let theBool = checkToSeeIfItemsEqual(container1: damnArray, container2: damnArray2)
print(theBool)
Any is not equatable, so you can't call checkToSeeIfItemsEqual using Any arrays.
I'm not sure what exactly you're trying to do, but I think you might be misunderstanding how equatable works. The types have to be known and identical on both sides of the expression. e.g. Bool == Bool. If you have an Array, you have no way of knowing what the type of the array's Element is, and so cannot compare them.
If you want to compare two arrays of the same equatable type, you can do this:
func arraysAreEqual<ElementType: Equatable>(firstArray: [ElementType], secondArray: [ElementType]) -> Bool {
guard firstArray.count == secondArray.count else {
return false
}
for i in 0..<firstArray.count{
if firstArray[i] != secondArray[i]{
return false
}
}
return true
}
Related
I am taking some inspiration from
https://marcosantadev.com/swift-arrays-holding-elements-weak-references/
and I want to be able to maintain an array holding weak references to its elements, so that in case those elements get released elsewhere in my code base, I don't retain them in my array.
I would like the implementation to be as type safe as possible, however should be reusable.
The strategy that I am using is declaring a Weak Reference container as so.
class WeakRefContainer<T> where T: AnyObject {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
Then I want to maintain an array of these WeakRefContainers, so I create an array extension:
extension Array where Element: WeakRefContainer<AnyObject> {
func compact() -> [WeakRefContainer<AnyObject>] {
return filter { $0.value != nil }
}
}
When calling the compact method, I am now able to clear up the array in case stuff needs to be cleaned up.
I am now having some compilation issues which am having trouble understanding.
Lets suppose I have a sample class
class SampleClass {
}
And I try to use everything as follows:
var weakReferencesArray = [WeakRefContainer<SampleClass>]()
let obj1 = WeakRefContainer.init(value: SampleClass())
let obj2 = WeakRefContainer.init(value: SampleClass())
weakReferencesArray.append(obj1)
weakReferencesArray.append(obj2)
weakReferencesArray.compact()
When I try to call compact I get the following error message:
MyPlayground.playground:29:21: 'WeakRefContainer<SampleClass>' is not a subtype of 'WeakRefContainer<AnyObject>'
Can anyone unblock me please? Thanks
Your code doesn't work because WeakRefContainer<SampleClass> is not a subclass of WeakRefContainer<AnyObject> because generics are invariant in Swift. Thus weakReferencesArray can't use the compact method added from the extension.
There is a workaround for this, via a protocol:
protocol WeakHolder {
var hasRef: Bool { get }
}
extension WeakRefContainer: WeakHolder {
var hasRef: Bool { return value != nil }
}
extension Array where Element: WeakHolder {
func compacted() -> [Element] {
return filter { $0.hasRef }
}
mutating func compact() {
self = compacted()
}
}
I also renamed compact to compacted, for better Swift semantics, and replaced the original compact by a mutating version.
You probably want the extension to apply to all [WeakRefContainer<T>] where T can be any type extending AnyObject.
extension Array where Element: WeakRefContainer<T> {
However, currently, parameterised extensions are not possible. See this proposal.
You can kind of work around this by making compact generic:
extension Array{
func compact<T>() -> [Element] where Element == WeakRefContainer<T> {
return filter { $0.value != nil }
}
}
I'm trying to create an extension of Array that returns a new array of unique items based on the items with the closure applied.
For example: If I had an array of Apple where apple has properties name and origin, to get one Apple of each origin I would call apple.uniqued(on: { $0.origin })
Here's the code I have so far:
extension Array where Element: Equatable {
func uniqued(on extract: (Element) -> Equatable) { // A
let elementsAndValues = map { (item: $0, extracted: extract($0)) } // 1
var uniqueValues: [Element] = []
var uniqueExtracts: [Equatable] = [] // A
elementsAndValues.forEach { (item, extracted) in
if !uniqueExtracts.contains(extracted) { // 3, B
uniqueValues += [item]
uniqueExtracts += [extracted]
}
}
return uniqueValues
}
}
This should work as follows:
Create an array of tuples with both the original elements (item) and the elements with the closure applied (extracted)
If uniqueExtracts doesn't contain the item, add it and add the item to the uniqueItems array.
The errors I'm getting are:
A) "Protocol 'SomeProtocol' can only be used as a generic constraint because it has Self or associated type requirements" (twice)
B) "Missing argument label 'where:' in call"
I'm using the latest version of Xcode. Any advice would be a lot of help. Many thanks.
You have multiple problems that mix together to create the errors you’re seeing. What you should do is use a generic.
extension Array
{
func uniqued<T:Equatable>(on extract:(Array.Element) -> T) -> [Array.Element]
{
let elementsAndValues = self.map{ (item: $0, extracted: extract($0)) }
var uniqueValues:[Element] = []
var uniqueExtracts:[T] = []
for (item, extracted) in elementsAndValues
{
if !uniqueExtracts.contains(extracted)
{
uniqueValues.append(item)
uniqueExtracts.append(extracted)
}
}
return uniqueValues
}
}
The <T:Equatable> declares a generic type parameter T that conforms to Equatable. Then the function signature can expect a closure that returns some generic type T that we know conforms to Equatable from the type constraint in the angle brackets. You also have to change every occurrence of Equatable to the generic parameter T, since Equatable isn’t a real type; see my answer here. If you do that the code should compile.
You have a few other things you should probably change:
Instead of using elementsAndValues.forEach(:), use a for <pattern> in list {} loop.
Although this is contentious, you should probably use the Array().append(:) method instead of += concatenation when adding one element to an array. In the case of +=, as opposed to +, this is purely to convey intent.
You did not declare a return type for your function, so the compiler assumes it returns Void, and so the return uniqueValues statement will cause a compiler error. Add a -> [Array.Element] to the function to fix this.
where Element:Equatable as a constraint on Array itself is superfluous. You are using a key function to determine equability, so whether the elements themselves are equatable is irrelevant.
You may want to use a Set or some other hashed data structure instead of a uniqueExtracts array. Testing for membership in an array is an O(n) operation.
I would do this with a group(by:) function, which would group each element in the sequence by a given key (e.g. the origin), yielding a Dictionary mapping keys to groups (arrays of elements in the group). From there, I would just map over the dictionary and just get the first element in each group.
public extension Sequence {
public typealias Element = Iterator.Element
public typealias Group = [Element]
public func group<Key: Hashable>(by deriveKey: (Element) -> Key) -> [Key: Group] {
var groups = [Key: Group]()
for element in self {
let key = deriveKey(element)
if var existingArray = groups[key] { // Group already exists for this key
groups[key] = nil //performance optimisation to prevent CoW
existingArray.append(element)
groups[key] = existingArray
}
else {
groups[key] = [element] // Create new group
}
}
return groups
}
}
struct Apple {
let name: String
let origin: String
}
let apples = [
Apple(name: "Foo", origin: "Origin 1"),
Apple(name: "Bar", origin: "Origin 1"),
Apple(name: "Baz", origin: "Origin 2")
]
let firstAppleInEachOrigin = apples.group(by: {$0.origin}).flatMap{ _, group in group.first }
firstAppleInEachOrigin.forEach{ print($0) }
Why isn't this working? I can use array.contains() on a String but it doesn't work for an Object.
var array = ["A", "B", "C"]
array.contains("A") // True
class Dog {
var age = 1
}
var dogs = [Dog(), Dog(), Dog()]
var sparky = Dog()
dogs.contains(sparky) // Error Cannot convert value of type 'Dog' to expected argument type '#noescape (Dog) throws -> Bool
Your Dog needs to implement Equatable.
class Dog: Equatable {
var age = 1
}
func == (lhs: Dog, rhs: Dog) -> Bool {
return lhs.age == rhs.age
}
To really explain what's happening there, first we have to understand there are two contains methods on Array (or better said, on SequenceType).
func contains(_ element: Self.Generator.Element) -> Bool
with constraints
Generator.Element : Equatable
and
func contains(#noescape _ predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Bool
The first one basically searches for a given element in the array using ==. The second one uses a closure that returns a Bool to search for elements.
The first method cannot be used because Dog doesn't adopt Equatable. The compiler tries to use the second method but that one has a closure as the parameter, hence the error you are seeing.
Solution: implement Equatable for Dog.
If you are looking for object reference comparison, you can use a simple closure:
let result = dogs.contains({ $0 === sparky })
Swift
If you are not using object then you can user this code for contains.
let elements = [ 10, 20, 30, 40, 50]
if elements.contains(50) {
print("true")
}
If you are using NSObject Class in swift. This variables is according to my requirement. you can modify for your requirement.
var cliectScreenList = [ATModelLeadInfo]()
var cliectScreenSelectedObject: ATModelLeadInfo!
This is for a same data type.
{ $0.user_id == cliectScreenSelectedObject.user_id }
If you want to AnyObject type.
{ "\($0.user_id)" == "\(cliectScreenSelectedObject.user_id)" }
Full condition
if cliectScreenSelected.contains( { $0.user_id == cliectScreenSelectedObject.user_id } ) == false {
cliectScreenSelected.append(cliectScreenSelectedObject)
print("Object Added")
} else {
print("Object already exists")
}
This answer isn't relevant for the OP's question, but might be helpful to others who are confronted with the Swift error message
Cannot invoke 'contains' with an argument list of type '(whatever)'
But first a quick quiz: Can you spot the problem here?
internal class FrameworkAdminConnections {
private var _localConnectionKeys = [Int]()
... other code omitted
public func isLocalConnection(_ connectionKey : Int) {
return _localConnectionKeys.contains(connectionKey)
}
}
Swift kept telling me I couldn't invoke contains() with an argument list of type (Int), which was a very unhelpful error message, and I don't dare admit how long it took me to finally figure it out.
The real problem was that Swift's inference engine couldn't figure out what the result of the contains() method should be - because I'd stupidly not specified "-> Bool" on the isLocalConnection() method signature!
Okay, so I have a stream that is receiving an array at a fast speed constantly. Here's what I want to do...
If the array is the same, don't do anything. If it is different, make a new array with nil as every value except the changed ones.
Example:
Incoming array 1: [1,1,1,1]
Incoming array 2: [1,1,2,1]
I want to create: [nil, nil, 2, nil]. Only marking the changes.
I made something that worked, I just don't think it's efficient. Is it the best way to do it?
var storedArray = [Int](count: 10, repeatedValue: 0) //array for comparing
func incomingArray(data: [Int]) {
if data == storedArray {return} //do nothing if its the same
var tempArray = [Int?](count: 10, repeatedValue: nil) //nil array
for index in 0...9 {
if storedArray[index] != data[index] {
tempArray[index] = data[index] //replace the temp array index
}
}
//send the completed tempArray to do work ....
storedArray = incomingArray //save the stored as the current data
}
So the above code works. It's just not efficient. Any better ideas for this?
thanks
UPDATE 1:
I have mistaken in the original post. Instead of Int. They are UInt8.
If you’re concerned about performance, the first thing to look for is hidden loops. Here’s one:
if data == storedArray {return}
This is presumably here for intended efficiency – if the two arrays are equal, don’t bother doing anything. But really, this might be self-defeating. That comparison isn’t constant time – it loops over the elements and compares them. Since you’re going to loop over them later anyway, that probably doesn’t give you much.
You could argue it saves you allocating a new array, but this then leads to the next question which is do you really need to create an array with all those nil values? Why not instead generate an array of the indices into the array that are different? That way, the recipient of your differences will only have to loop over the differences (maybe only a couple) rather than the whole array.
It probably makes sense to factor out the array diffing from the processing and storage. Here’s a function that takes two arrays and returns an array of indices where they differ:
func differences<T: Equatable>(lhs: [T], rhs: [T]) -> [Int] {
// indexedPairs is a sequence of (index, (left-hand val, right-hand val))
let indexedPairs = enumerate(zip(lhs,rhs))
// the lazy may or may not help here, benchmark to find out...
return lazy(indexedPairs).filter { (index, pair) in
// only return different pairs
pair.0 != pair.1
}.map {
// only return the index not the values
$0.0
}.array
}
Note this is a pure function – that is, it takes inputs and produces a result without referencing any external state. This makes it easier to test and debug as a standalone function.
You could then rewrite your original function in terms of it:
func incomingArray(data: [Int]) {
let diffs = differences(storedArray, data)
if !diffs.isEmpty {
// send new data and diff indices for further processing
// then overwrite the old array
storedArray = data
}
}
Update
Benchmarking suggests the filter/map version performs horribly, compared to a simple loop, so here’s a version of differences that just uses for…in:
func differences<T: Equatable>(lhs: [T], rhs: [T]) -> [Int] {
var diffs: [Int] = []
// still using zip, since this guards against the two
// arrays being of different sizes - doesn’t seem to
// impact performance
for (i,(l,r)) in zip(indices(lhs),zip(lhs,rhs)) {
if l != r { diffs.append(i) }
}
return diffs
}
Some quick tests suggests this version gets a significant speedup if the input is large and the # of differences small, but performs identically if the arrays are mostly different.
Here are some ideas to make this code faster:
1) Instead of using an array of Int?, use a plain Int and instead of marking elements as nil, mark them as some special integer value. I don't know what that value is, maybe 0 is fine, or -1, or Int.max.
Update: The above change gives me a ~ 10% performance increase
2) Recycle your result array. So that you can skip the following code:
var tempArray = [Int?](count: 10, repeatedValue: nil)
Or maybe better, let the caller pass it in via an inout parameter so that you don't have to worry about ownership of it.
Update: The above change gives me a ~ 50% performance increase
Here is the code for all the versions suggested in this question:
import UIKit
import XCTest
var storedArray1 = [Int?](count: 10, repeatedValue: 0) //array for comparing
func processIncomingArray1(data: [Int]) {
var tempArray = [Int?](count: 10, repeatedValue: nil) //nil array
for index in 0...9 {
if storedArray1[index] != data[index] {
tempArray[index] = data[index] //replace the temp array index
}
}
storedArray1 = tempArray
}
var storedArray2 = [Int](count: 10, repeatedValue: 0)
func processIncomingArray2(data: [Int]) {
var tempArray = [Int](count: 10, repeatedValue: Int.max)
for index in 0...9 {
if storedArray2[index] != data[index] {
tempArray[index] = data[index]
}
}
storedArray2 = tempArray
}
var storedArray3 = [Int](count: 10, repeatedValue: Int.max)
func processIncomingArray3(data: [Int], inout result: [Int]) {
for index in 0...9 {
if result[index] != data[index] {
result[index] = data[index]
}
}
}
// Given two sequences, return a sequence of 2-tuples (pairs)
public func zip<A: SequenceType, B: SequenceType>(a: A, b: B)
-> ZipSequence<A, B>
{
return ZipSequence(a, b)
}
// Lazy sequence of tuples created from values from two other sequences
public struct ZipSequence<A: SequenceType, B: SequenceType>: SequenceType {
private var a: A
private var b: B
public init (_ a: A, _ b: B) {
self.a = a
self.b = b
}
public func generate() -> ZipGenerator<A.Generator, B.Generator> {
return ZipGenerator(a.generate(), b.generate())
}
}
// Generator that creates tuples of values from two other generators
public struct ZipGenerator<A: GeneratorType, B: GeneratorType>: GeneratorType {
private var a: A
private var b: B
public init(_ a: A, _ b: B) {
self.a = a
self.b = b
}
mutating public func next() -> (A.Element, B.Element)? {
switch (a.next(), b.next()) {
case let (.Some(aValue), .Some(bValue)):
return (aValue, bValue)
default:
return nil
}
}
}
func differences<T: Equatable>(lhs: [T], rhs: [T]) -> [Int] {
// indexedPairs is a sequence of (index, (left-hand val, right-hand val))
let indexedPairs = enumerate(zip(lhs,rhs))
// the lazy may or may not help here, benchmark to find out...
return lazy(indexedPairs).filter { (index, pair) in
// only return different pairs
pair.0 != pair.1
}.map {
// only return the index not the values
$0.0
}.array
}
var storedArray4 = [Int](count: 10, repeatedValue: Int.max)
func processIncomingArray4(data: [Int]) {
let diffs = differences(storedArray4, data)
if !diffs.isEmpty {
// send new data and diff indices for further processing
// then overwrite the old array
storedArray4 = data
}
}
func differences5<T: Equatable>(lhs: [T], rhs: [T]) -> [Int] {
var diffs: [Int] = []
// still using zip, since this guards against the two
// arrays being of different sizes - doesn’t seem to
// impact performance
for (i,(l,r)) in zip(indices(lhs),zip(lhs,rhs)) {
if l != r { diffs.append(i) }
}
return diffs
}
var storedArray5 = [Int](count: 10, repeatedValue: Int.max)
func processIncomingArray5(data: [Int]) {
let diffs = differences5(storedArray4, data)
if !diffs.isEmpty {
// send new data and diff indices for further processing
// then overwrite the old array
storedArray5 = data
}
}
class StackOverflowTests: XCTestCase {
func testPerformanceExample1() {
var data = [1,2,3,4,5,6,7,8,9,10]
self.measureBlock() {
for i in 1...100000 {
processIncomingArray1(data)
}
}
}
func testPerformanceExample2() {
var data = [1,2,3,4,5,6,7,8,9,10]
self.measureBlock() {
for i in 1...100000 {
processIncomingArray2(data)
}
}
}
func testPerformanceExample3() {
var data = [1,2,3,4,5,6,7,8,9,10]
self.measureBlock() {
for i in 1...100000 {
processIncomingArray3(data, &storedArray3)
}
}
}
func testPerformanceExample4() {
var data = [1,2,3,4,5,6,7,8,9,10]
self.measureBlock() {
for i in 1...100000 {
processIncomingArray4(data)
}
}
}
func testPerformanceExample5() {
var data = [1,2,3,4,5,6,7,8,9,10]
self.measureBlock() {
for i in 1...100000 {
processIncomingArray5(data)
}
}
}
}
I think I have the best answer. Instead of the whole empty nil array. I made my temp array a bool value. Then if the value changes, mark its index as true.
So here's a example.
Incoming array 1: [1,1,1,1]
Incoming array 2: [1,1,2,1]
I then export the full array plus a bool array of: [false, false, true, false].
Then I just check if theres a change and pull the value. It turned out to work much faster then the other answers. I also recycled the temp array to speed it up. My guess is that since it's value can only be true or false, it's a lot faster then NIL/UInt8.
Thanks for the help guys. Let me know if any other ideas come up.
I just want to convert an array into a tuple in Swift; something like the following:
>>> myArray = [1,2,3,4,5]
>>> mytuple = tuple(myArray)
>>> mytuple
(1, 2, 3, 4, 5)
What's the easiest way of doing that?
(I only started learning Swift today, so I am very, very new).
It's actually quite possible, if you are willing to do some low level magic. I don't think it works if the tuple has a collection type, though. This is mainly for interacting with C libraries.
typealias TupleType = (UInt8, UInt8, UInt8)
var array = [2, 3, 4] as [UInt8]
var tuple = UnsafeMutablePointer<StepsType>(malloc(UInt(sizeof(TupleType))))
memcpy(tuple, array, UInt(array.count))
More of this stuff can be found here:
https://codereview.stackexchange.com/questions/84476/array-to-tuple-in-swift/84528#84528
Because 70% of us are highly visual beings:
You can't do this because the size of a tuple is part of its type, and this isn't true for an array. If you know the array's length, you can just do let myTuple = (myArray[0], myArray[1], ...). Or you can build your architecture around tuples from the beginning. What are you trying to do?
This is a common problem which occurs in lots of other languages. See How do I convert a list to a tuple in Haskell?
if what you want to do is extract multiple value from array at the same time, maybe
the following code could help you
extension Array {
func splat() -> (Element,Element) {
return (self[0],self[1])
}
func splat() -> (Element,Element,Element) {
return (self[0],self[1],self[2])
}
func splat() -> (Element,Element,Element,Element) {
return (self[0],self[1],self[2],self[3])
}
func splat() -> (Element,Element,Element,Element,Element) {
return (self[0],self[1],self[2],self[3],self[4])
}
}
then you can use it like this
let (first,_,third) = ( 0..<20 ).map { $0 }.splat()
you can even write a codegen to generate the extension code
This my universal solution for copy array data to tuple. The only problem that I could not solve is to check the type of the element in the tuple and the element from the array.
/// Unsafe copy array to tuple
func unsafeCopyToTuple<ElementType, Tuple>(array: inout Array<ElementType>, to tuple: inout Tuple) {
withUnsafeMutablePointer(to: &tuple) { pointer in
let bound = pointer.withMemoryRebound(to: ElementType.self, capacity: array.count) { $0 }
array.enumerated().forEach { (bound + $0.offset).pointee = $0.element }
}
}
Scared of unsafe pointers? Or need support for tuples with different types inside?
It can also be done like this:
public extension Array {
public var tuple: Any? {
switch count {
case 0:
return ()
case 1:
return (self[0])
case 2:
return (self[0], self[1])
case 3:
return (self[0], self[1], self[2])
case 4:
return (self[0], self[1], self[2], self[3])
case 5:
return (self[0], self[1], self[2], self[3], self[4])
default:
return nil
}
}
Because typing this out can be a little error prone, it's a good idea to write tests for this that make sure the right tuple is returned.
I've used this approach in ArrayPlusTuple.
So instead of writing this functionality yourself you could just add pod 'ArrayPlusTuple' to your pod file and get this tested functionality for free.