[swift-users] Subtract a set of a subclass?

Zhao Xin owenzx at gmail.com
Thu Sep 1 17:44:46 CDT 2016


Hi Nick,

Glad to help.

but when using third party classes I don’t know if the hash values are
> comparable
>

You can create an extension with a convenient init(:), which creates a new
instance of  the super class basing on the instance of the sub class. That
will guarantee the subtraction. Below code works in Xcode 7.3.1 with Swift
2.2.

import Foundation


func ==(lhs: Foo, rhs: Foo) -> Bool {

    return lhs.id == rhs.id

}


class Foo:Hashable {

    let id:Int

    var hashValue: Int {

        return id

    }



    required init(_ id:Int) {

        self.id = id

    }

}


class Bar:Foo {

    override var hashValue: Int {

        return id * 5

    }

}


var fooSet:Set<Foo> = [Foo(10), Foo(9), Foo(8), Foo(7)]

var barSet:Set<Bar> = [Bar(8), Bar(7), Bar(6), Bar(5)]


//fooSet.subtract(barSet) // error: cannot invoke 'subtract' with an
argument list of type '(Set<Bar>)'

fooSet = fooSet.subtract(barSet as Set<Foo>) // works, but not what we want

fooSet.forEach { print("\($0.dynamicType), id:\($0.id)") }

/*

 Foo, id:7

 Foo, id:10

 Foo, id:9

*/


Extension part.  You should comment above subtracting code before you run
below code, as you want to make sure the result is not polluted.


extension Foo {

    convenience init(_ instance:Foo) {

        self.init(instance.id)

    }

}


let anotherFooSet = { () -> Set<Foo> in

    var set = Set<Foo>()

    for element in barSet {

        let foo = Foo(element)

        set.insert(foo)

    }



    return set

}()


fooSet = fooSet.subtract(anotherFooSet)

fooSet.forEach { print("\($0.dynamicType), id:\($0.id)") }

/*

 Foo, id:10

 Foo, id:9

*/


Zhaoxin

On Thu, Sep 1, 2016 at 9:41 PM, Nick Brook <nrbrook at gmail.com> wrote:

> Hi Zhao
>
> Thanks for your response.
>
> I understand your point, but when using third party classes I don’t know
> if the hash values are comparable, but for example I may want to have a set
> of ‘data' (NSData) and a subset of ‘mutable data' (NSMutableData), which
> point to the same objects. As a user of swift I would expect to be able to
> subtract Set<NSMutableData> from Set<NSData>.
>
> Your last example perhaps works in Swift 3, so this may be fixed now, but
> in Swift 2 you get the error
>
> Cannot invoke 'subtract' with an argument list of type '(Set<Bar>)’
>
> Perhaps Swift 3 supports it with some additional safety around hashValue
> overriding or something.
>
> Thanks
>
> Nick
>
> On 1 Sep 2016, at 04:00, Zhao Xin <owenzx at gmail.com> wrote:
>
> I don't see the point. For example if an element in Set<B> and another
> element in Set<A> are with a same hash value. Neither of the elements
> should be subtracted. As they are in different types. And hash values
> between different types are not guaranteed to be comparable.
>
> import Foundation
>
> class Foo:Hashable {
>     var value: Int
>
>
>     public var hashValue: Int {
>         return value
>     }
>
>
>     public static func ==(lhs: Foo, rhs: Foo) -> Bool {
>         return lhs.value == rhs.value
>     }
>
>
>     required init(_ value:Int) {
>         self.value = value
>     }
> }
>
> class Bar:Foo {
>     override public var hashValue: Int {
>         return value * 10
>     }
> }
>
> let foo = Foo(10)
> let bar = Bar(10)
>
> print(foo.hashValue) // 10
> print(bar.hashValue) // 100
> print((bar as Foo).hashValue) // 100 instead of 10
>
> print(foo == bar) // true
> print(foo.hashValue == bar.hashValue) // false
>
> As you can see in the above code, although `foo == bar` is true,
> `foo.hashValue == bar.hashValue` is not guaranteed to be true. As far as I
> know, Set<T> uses hash values instead of equations to compare the elements.
> So the results of a super class and its sub class are not guaranteed. Also,
> as `(bar as Foo).hashValue` is always the result of its own class, you
> can't get the results you want through casting.
>
> var fooSet:Set<Foo> = [Foo(10), Foo(9), Foo(8), Foo(7)]
> var barSet:Set<Bar> = [Bar(8), Bar(7), Bar(6), Bar(5)]
>
> fooSet.subtract(barSet)
> fooSet.forEach { print("\(type(of:$0)), value:\($0.value)") }
> /*
>  Foo, value:10
>  Foo, value:9
>  Foo, value:8 // Here is a mystery, Foo(7) is unreasonably missing.
>
> */
>
> However, if you can guarantee the hash values are comparable, you still
> can get the results you want.
>
> class Foo:Hashable {
>     var value: Int
>
>
>     public var hashValue: Int {
>         return value
>     }
>
>
>     public static func ==(lhs: Foo, rhs: Foo) -> Bool {
>         return lhs.value == rhs.value
>     }
>
>
>     required init(_ value:Int) {
>         self.value = value
>     }
> }
>
> class Bar:Foo {
>     var name = "bar"
> }
>
> var fooSet:Set<Foo> = [Foo(10), Foo(9), Foo(8), Foo(7)]
>
> var barSet:Set<Bar> = [Bar(8), Bar(7), Bar(6), Bar(5)]
>
> fooSet.subtract(barSet)
> fooSet.forEach { print("\(type(of:$0)), value:\($0.value)") }
> /*
>  Foo, value:10
>  Foo, value:9
>
> */
>
>
> Zhaoxin
>
> On Thu, Sep 1, 2016 at 8:31 AM, Nick Brook via swift-users <
> swift-users at swift.org> wrote:
>
>> I have a set, Set<A> and a subset of that, Set<B>, where B: A. I want to
>> subtract Set<B> from Set<A>, but the subtract function signature specifies
>> that the set elements must be the same type (S.Generator.Element ==
>> Element). I guess this is because Element is not required to be a class,
>> simply hashable, therefore inheritance is not guaranteed? Is there any way
>> this could be implemented in Set, in an extension, or what would be the
>> most efficient way to perform that operation?
>>
>> Thanks
>>
>> Nick
>>
>> _______________________________________________
>> swift-users mailing list
>> swift-users at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-users
>>
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20160902/5d778652/attachment.html>


More information about the swift-users mailing list