Cannot create proper Equatable function for usage with Array's .contains() - ios

I am trying to check whether an array of tuples contains a certain tuple using the native contains() method of Array.
I have declared my two "equatable" functions as
public func ==(a: (clip1: Clip?, clip2: Clip?), b: (clip1: Clip?, clip2: Clip?)) -> Bool {
let clipa1 = a.clip1
let clipa2 = a.clip2
let clipb1 = b.clip1
let clipb2 = b.clip2
if clipa1 != nil && clipa2 != nil && clipb1 != nil && clipb2 != nil {
return (clipa1! == clipb1!) && (clipa2! == clipb2!)
}
else if clipa1 != nil && clipa2 == nil && clipb1 != nil && clipb2 == nil {
return (clipa1! == clipb1!)
} else {
return false
}
}
public func ==(a: Clip, b: Clip) -> Bool {
return a.id == b.id
}
However when I try these in this manner
for clip in tmp {
if !_filteredClips?.contains((clip1: clip.clip1, clip2: clip.clip2)) {
_filteredClips?.append(clip)
}
}
I am getting Cannot convert value of type '(clip1: Optional<Clip>, clip2: Optional<Clip>)' to expected argument type '#noescape ((clip1: Clip?, clip2: Clip?)) throws -> Bool'
What am I missing here?

The version of contains you are trying to use is only available for elements that adopt Equatable:
extension SequenceType where Generator.Element : Equatable {
/// Return `true` iff `element` is in `self`.
#warn_unused_result
public func contains(element: Self.Generator.Element) -> Bool
}
The tuple type (Clip?, Clip?) does not and you can't extend it. Since you've implemented ==, you can use the version of contains that takes a predicate like this.
for clip in tmp {
if !_filteredClips!.contains({ $0 == clip }) {
_filteredClips?.append(clip)
}
}
See How do I check if an array of tuples contains a particular one in Swift? for other ideas.

== can be used to test the equality of custom types such as structures, classes and tuples as the caller is doing here. The choice between == and === depends on whether your code needs to test for equality or identity, not on whether you are working with a class, struct, etc

Related

Dart, overriding not equal operator gives error

I've been developing dart(flutter) for some while and I came upon this Error. I was creating a custom class that had several operator overrides. For explanation purposes, the class looks like this.
class CustomObject {
int big;
int small;
CustomObject(this.big, this.small);
#override
bool operator ==(Object other) {
if (other is CustomObject) {
return big == other.big && small == other.small;
}
return false;
}
#override
int get hashCode => big.hashCode ^ small.hashCode;
#override
bool operator !=(Object other) {
// Error: The string '!=' isn't user-definable operator.
if (other is CustomObject) {
return big != other.big || small != other.small;
}
return false;
}
}
The error occurs on the != operator override. Looking at this website https://www.geeksforgeeks.org/operator-overloading-in-dart/ it says that you can override != operator. I could not find any other source that documents overriding this operator.
My question is, 1. are you supposed to override != in the first place? 2. if so, is are any restrictions on overriding != operators?

Null-aware operator with Maps

I had the same problem with lists, now it is Map.
What I would like to do
The following syntax is not Dart, as in it does not compile:
map?[key] ?? otherValue
If my map was not a Map but a List, it would look like Günter pointed out here:
list?.elementAt(index) ?? otherValue
What I am searching for
I understand that map?[key] is not valid syntax and therefore I am searching for something like elementAt, which works for lists, for maps.
map?.valueFor(key) ?? otherValue
valueOf
That does obviously not yet exist. The problem has solutions and valueOf might be a good one as well.
This works:
(map ?? const {})[key] ?? otherValue;
Because the key will fallback to accessing an empty Map, which will always return null.
I use this function for null-safe nested map access:
// Returns value of map[p1][p2]...[pn]
// where path = [p1, p2, ..., pn]
//
// example: mapGet(map, ["foo", 9, 'c'])
dynamic mapGet(Map map, List path) {
assert(path.length > 0);
var m = map ?? const {};
for (int i = 0; i < path.length - 1; i++) {
m = m[path[i]] ?? const {};
}
return m[path.last];
}
Something like this might work as an extension function:
extension MapExtensions<T, K> on Map<T, K> {
K getOrNull(T key) {
if (this == null || !this.containsKey(key)) {
return null;
} else {
return this[key];
}
}
K getOrElse(T key, K fallback) {
return this.getOrNull(key) ?? fallback;
}
}
Note: I haven't tested this, but should work.
Yet another syntax I used a few times:
map?.containsKey(key) ?? false ? map[key] : otherValue
Although functionally equivalent to the one proposed by #Günter Zöchbauer it is a bit more cumbersome.
Anyway, since it might look clearer in a few circumstances, it is worth to mention.
Or
map != null ? map[key]: otherValue;
which checks to see if the map is null before attempting to access the variable, which is the same as Günter's answer but checks first.
Based on Arturs answer, I think we should improve it and discuss alternative implementations.
Extension on non-null Map<K,V>
In extensions on non-nullable types this is never null, so we should not check against it. (see bottom for more information)
I use K for the key type and V for the value type
make all parameters and return values nullable.
extension MapExt<K, V> on Map<K, V> {
V? getOrNull(K? key) {
if (!containsKey(key) || key == null) {
return null;
} else {
return this[key];
}
}
V? getOrElse(K? key, V? fallback) {
return getOrNull(key) ?? fallback;
}
}
Null-safe usage would then look like this:
myMap?.getOrNull(someKey)
Note that it is required to put a ? here because the extensions is implemented on non-null maps, which is consistent with the rest of the nullsafe-apis.
(Not really an ) alternative: implement extension on nullable Map<K,V>?
It is possible to implement the extension on nullable maps (i.e. Map<K,V>?) which then would require to check this for null:
extension MapExt<K, V> on Map<K, V>? {
V? getOrNull(K? key) {
if (this == null || !this!.containsKey(key) || key == null) {
return null;
} else {
return this![key];
}
}
V? getOrElse(K? key, V? fallback) {
return getOrNull(key) ?? fallback;
}
}
This implementation allows accessing the extension methods on a potential null map directly without ?:
Map<int,int>? = ...
myMap.getOrNull(someKey); // no compiler error
I think this is not consistent with the rest of the null-safe operators. I would suggest not using it, but preferring the first variant, which reveals that the map might be null.
I have written my own version based on the answers above. It is used mostly for the JSON object handling.
dynamic mapOrListGet(dynamic map, List path) {
assert(path.length > 0);
assert(map is List || map is Map);
var m = map ?? const {};
var firstEl = path.removeAt(0);
var result = (m is List)
? (firstEl < m.length ? m.elementAt(firstEl) : null)
: m[firstEl];
if (path.length == 0 || result == null) {
return result;
} else {
return mapOrListGet(result, path);
}
}
And here is how it works.

Swift 3: Cannot invoke (protocol method) with argument list of type (generic)

Given this protocol:
import Foundation
protocol Updatable {
associatedtype E
func updateValuesWith(_ newElement: E)
}
And this implementation:
private func update<T: NSObject>(_ values: inout [T], with newValues: [T]) where T: Updatable {
guard values.count != 0 else { values = newValues; return }
for newElement in newValues {
let newIndex = newValues.index(of: newElement)
if values.contains(newElement) {
let oldIndex = values.index(of: newElement)
values[oldIndex!].updateValuesWith(newElement) //<--- PROBLEM HERE
if oldIndex != newIndex {
swap(&values[oldIndex!], &values[newIndex!])
}
} else {
values.insert(newElement, at: newIndex!)
}
}
for element in values where values.index(of: element)! > newValues.count {
values.remove(at: values.index(of: element)!)
}
}
I don't get why I am getting this error message
Cannot invoke 'updateValuesWith' with an argument list of type '(T)'
Expected an argument list of type '(T.E)'
on line values[oldIndex!].updateValuesWith(newElement).
After some unfruitful research I am convinced I must be missing something very basic but still can't figure out what exactly.
Enlighten me please?
You are declaring input parameter type T to be Updatable, but your updateValuesWith does not take T. It takes another type E which you are not specifying at all. If you want your code to compile as is, you must tell the compiler what is E supposed to be:
... where T: Updatable, T.E == T
which is kinda strange because i have no idea how updateValuesWith knows what to do, when newElement does not implement any useful interface (except force casting it to what you expect it to be). But that would be a different question...

Optional and non-optional not able to be returned from function that returns an optional

I'm working on writing a function that returns what the next node of a tree would be from any node, if doing an in-order traversal. The function has an optional return, which is needed if there is no next node (i.e. we're at the last node). I return both an optional and non-optional from the function and at both places there are compiler errors. Here is the code:
class TreeNode<T>
{
var leftChild : TreeNode?
var rightChild : TreeNode?
var parent : TreeNode?
var value : T
init(withValue:T) {
self.value = withValue
}
func nextInOrderNode<T>() -> TreeNode<T>?
{
if var nextNode = rightChild
{
while nextNode.leftChild != nil {
nextNode = nextNode.leftChild!
}
return nextNode //error 1
}
else
{
var nextNode = parent
var currentNode = self
while nextNode?.rightChild !== currentNode {
currentNode = nextNode!
nextNode = nextNode?.parent
}
return nextNode //error 2
}
}
}
The errors are:
error 1: Cannot convert return expression of type 'TreeNode<T>' to return type 'TreeNode<T>?'
error 2: Cannot convert return expression of type 'TreeNode<T>?' to return type 'TreeNode<T>?'
I'm not sure what I'm doing wrong here because from what I've seen, it seems like this should work. Any help will be greatly appreciated.
The problem is unrelated to optional vs non-optional return values.
In
func nextInOrderNode<T>() -> TreeNode<T>?
you introduce a new placeholder type T, locally to this method,
which is unrelated to (and hides) the placeholder type T
in class TreeNode<T>.
The solution is simple:
just remove the placeholder in the method declaration:
func nextInOrderNode() -> TreeNode<T>?
You don't need it here because the scope of T in class TreeNode<T>
is the entire class definition, including all methods.

Swift: Overloaded == operator being used on another type

I have overloaded the "==" operator for a class called PBExcuse, but when trying to compare EKSourceType objects the compiler tries to use my PBExcuse overloaded operator and won't compile. The error message is: "'EKSourceType' is not convertible to 'PBExcuse'".
Here is the applicable code:
Where Comparing:
for (var i = 0; i < eventStore.sources().count; i++) {
let source:EKSource = eventStore.sources()[i] as EKSource
let currentSourceType:EKSourceType = source.sourceType
let sourceTypeLocal:EKSourceType = EKSourceTypeLocal
if (currentSourceType == sourceTypeLocal){ //something is wrong here!!
calendar.source = source;
println("calendar.source \(calendar.source)")
break;
}
}
In PBExcuse.swift:
func == (left:PBExcuse, right:PBExcuse) -> Bool{
if (left.event == right.event && left.title == right.title && left.message == right.message){
return true
}
return false
}
final class PBExcuse:NSObject, NSCoding, Equatable, Hashable{...}
EKSourceType is a struct
struct EKSourceType {
init(_ value: UInt32)
var value: UInt32
}
so you can only compare its value property:
if (currentSourceType.value == sourceTypeLocal.value) { ... }
The compiler message is misleading. Since == is not defined for EKSourceType,
the compiler tries to convert the struct to some other type for which == is defined.
Without your custom PBExcuse class, the error message would be
'EKSourceType' is not convertible to 'MirrorDisposition'
Note that you can simplify the loop slightly to
for source in eventStore.sources() as [EKSource] {
if source.sourceType.value == EKSourceTypeLocal.value {
// ...
}
}

Resources