2

I am trying to filter an array containing classes so that only the class that is found in another array will be added to an array. This is what I have so far:

class Match : Equatable {
var name: String
var value: String

init(name: String, value: String) {
    self.name = name
    self.value = value
}

func ==(lhs: Match, rhs: Match) -> Bool {
   return lhs.name == rhs.name && lhs.value == rhs.value
}

// attempt to filter array containing Match structs

let terms = [Match]()
let someOtherObjects = [Match]()
let sampleMatch = Match(name: "someName", value: "someValue")
someOtherObjects.append(sampleMatch)

filteredTerms = terms.filter { term in
   if attemptedCombos.contains(sampleMatch) {
      return true
   }
}

However the compiler does not let me build with the error:

"Cannot convert value of type 'Match' to expected argument type '@noescape (Match) throws -> Bool'

Any ideas?

4
  • Create a class instead of a struct and make it equatable Commented Jan 14, 2016 at 20:17
  • @LeoDabus changed question but still unable to work Commented Jan 14, 2016 at 20:23
  • 1
    How are terms and attemptedCombos declared? Why is the closure parameter term not used at all? – In any case: The closure must return a Bool (and not only inside the if-statement). Commented Jan 14, 2016 at 20:30
  • My tip: start with a super simple filter: let filteredTerms = terms.filter { term in return true }. Then add and remove more statements and expressions, until you figure out where the problem is. Commented Jan 14, 2016 at 20:32

2 Answers 2

0

Updated using Set (as it seems as if you want the intersection of two [Match] arrays). In addition to Equatable, you must let your Match class conform to Hashable for instances of it to be allowed as elements in a Set.

class Match : Equatable, Hashable {
    var name: String
    var value: String

    init(_ name: String, _ value: String) {
        self.name = name
        self.value = value
    }

    var hashValue: Int {
        get {
            return name.hashValue << 20 + value.hashValue
        }
    }
}

func ==(lhs: Match, rhs: Match) -> Bool {
    return lhs.name == rhs.name && lhs.value == rhs.value
}

Example:

/* Example */
var mySetA : Set<Match> = [Match("foo", "bar"), Match("foo", "foo"), Match("barbar", "foo")]

var mySetB = Set<Match>()
mySetB.insert(Match("barbar", "bar"))
mySetB.insert(Match("bar", "foo"))
mySetB.insert(Match("foo", "bar"))
mySetB.insert(Match("foo", "foo"))

let myIntersect = mySetA.intersect(mySetB)
for match in myIntersect {
    print("name: " + match.name + ", value: " + match.value)
}
/* name: foo, value: foo
   name: foo, value: bar  */

After chatting with the OP we've solved the issue in chat. I'm not sure what the convention are here, but I'll summarize the additional information given by the OP in chat, and the solution to the problem. Consider the block above as a solution to the question above, and the block below as a quite narrow solution to the question above complemented with more specifics from the OP.


  • The "filter" array of objects are of a different class type (Tern) than the array to be filtered (Match), where these two classes share some class properties.
  • A natural enquiry to the OP was if it was acceptable to let both these classes have one common superclass; it was.
  • In addition to the above, one of the properties common for both classes were of a custom enum type, posted in chat by the author.

The final solution used, as above, Set and .intersect():

/* custom enum given by OP in chat */
enum Declension : String {
    case firstDeclensionFem = "a:a:am:ae:ae:a:ae:ae:as:arum:is:is"
    case secondDeclensionMasc = "us:er:um:i:o:o:i:i:os:orum:is:is"
    case secondDeclensionNeu = "um:um:um:i:o:o:a:a:a:orum:is:is"
    case thirdDeclensionMasc = " : :em:is:i:e:es:es:es:um:ibus:ibus"
    case thirdDeclensionMascSpecial = " : :em:is:i:e:es:es:es:ium:ibus:ibus"
    case fourthFem = "us:us:um:us:ui:u:us:us:us:uum:ibus:ibus"
    case fourthNeu = "u:u:u:us:u:u:ua:ua:ua:uum:ibus:ibus"
    case fifthMasc = "es:es:em:ei:ei:e:es:es:es:erum:ebus:ebus"
    case unknown

    static let allValues = [firstDeclensionFem, secondDeclensionMasc, secondDeclensionNeu, thirdDeclensionMasc, thirdDeclensionMascSpecial, fourthFem, fourthNeu, fifthMasc]
}

/* use a superclass and let the sets below have members that
   are declared to be of this superclass type                 */
class MyMatchTypes : Equatable, Hashable {
    var latin: String
    var declension: Declension

    init(_ latin: String, _ declension: Declension) {
        self.latin = latin
        self.declension = declension
    }

    var hashValue: Int {
        get {
            return latin.hashValue << 20 + declension.hashValue
        }
    }
}

func ==(lhs: MyMatchTypes, rhs: MyMatchTypes) -> Bool {
    return lhs.latin == rhs.latin && lhs.declension == rhs.declension
}

/* the two classes mentioned in chat: use as subclasses */
class Term : MyMatchTypes {
    var meaning: String
    var notes: String
    var genStem: String

    init(_ latin: String, _ declension: Declension, _ meaning: String, _ genStem: String, _ notes: String) {

        self.meaning = meaning
        self.notes = notes
        self.genStem = genStem

        super.init(latin, declension)

    }
}

class Match : MyMatchTypes {

    // ... add stuff

    // super init is OK
}

/* Example                                         */
/* ----------------------------------------------- */
/* Set of `Match` objects */
var mySetA = Set<MyMatchTypes>()
mySetA.insert(Match("foo", Declension.firstDeclensionFem))
mySetA.insert(Match("bar", Declension.fourthFem))
mySetA.insert(Match("foofoo", Declension.fourthFem))
mySetA.insert(Match("barbar", Declension.fifthMasc))

/* Set of `Term` objects */
var mySetB = Set<MyMatchTypes>()
mySetB.insert(Term("fooshy", Declension.fourthFem, "a", "b", "c"))
mySetB.insert(Term("barbar", Declension.fifthMasc, "a", "b", "c"))
mySetB.insert(Term("bar", Declension.fourthFem, "a", "b", "c"))
mySetB.insert(Term("foofoo", Declension.firstDeclensionFem, "a", "b", "c"))
mySetB.insert(Term("foobar", Declension.fourthFem, "a", "b", "c"))

/* compute intersection */
let myIntersect = mySetA.intersect(mySetB)
for obj in myIntersect {
    print("latin: " + obj.latin + ", declension: \(obj.declension)")
}
/* latin: barbar, declension: fifthMasc
   latin: bar,    declension: fourthFem    */
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you for your help! The problem I am having is that the myFilter var you used above is part of an array. I want to find matching objects in two arrays. How would this change?
@AlexanderMacLeod I see. Possibly using a set structure would be more appropriate then, allowing for use of methods such as intersect().
@AlexanderMacLeod Added the Set solution.
Thank you very much for your edit. I am trying to convert my array to a set now: cannot invoke initialiser for type <Set> with the argument list type of match([Match])
@AlexanderMacLeod Happy to help. Note that in my example above I first initialize the set(s) using var ... = Set<Match>(), and after that I add Match objects one at a time into the set using insert. From your error message, it sounds as if you're trying to create a set where the members themselves are arrays of Match, rather than single Match objects.
|
0

If Match confirms to Equatable protocol your code should work. Following code compiles successfully:

class Match : Equatable {
    var name: String
    var value: String

    init(name: String, value: String) {
        self.name = name
        self.value = value
    }
}

func ==(lhs: Match, rhs: Match) -> Bool {
    return lhs.name == rhs.name && lhs.value == rhs.value
}

let sampleMatch1 = Match(name: "someNameA", value: "someValue")
let sampleMatch2 = Match(name: "someNameA", value: "someValue")
let sampleMatch3 = Match(name: "someNameB", value: "someValue")
let sampleMatch4 = Match(name: "someNameC", value: "someValue")

let terms = [sampleMatch1, sampleMatch3]
var someOtherObjects = [sampleMatch2, sampleMatch4]


let filteredTerms = terms.filter { term in
    return someOtherObjects.contains(term)
}

Result is:

enter image description here

5 Comments

contains({ $0 == sampleMatch }) and contains(sampleMatch) are both valid, and equivalent if Match conforms to Equatable (which is the case here).
@MartinR I have just checked - contains(sampleMatch) does not compile with the same error the TO has.
I don't know how attemptedCombos is declared, but if it is a [Match] array then it does compile (with the equatable Match class from the updated question).
@MartinR Thank you! As you may see by my edit, the filter needs to find a matching pair of classes from two arrays. Is this possible with your code?
@MartinR Yes, you a right. It seems I have initially implemented Equatable the wrong way. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.