0

I am struggling with filtering an array of objects which have a nested arrays of different objects.

Eg. I have an array of objects:

class Form {
   let name: String
   let lasname: String
   let roles: [Rule]

   // ... init etc.
}

class Rule {
   let rule: String

   // ... init etc.
}

Initialized array would looks like:

var forms = [Form]()
let firstRoles: [Rule] = [Rule(rule: "First"), Rule(rule: "Second")]
let secondRoles: [Rule] = [Rule(rule: "Third"), Rule(rule: "Second")]

forms.append(Form(name: "Test1", lastname: "Test2", roles: firstRoles))
forms.append(Form(name: "Test3", lastname: "Test4", roles: secondRoles))

And now I need to filter forms via Keys.

Keys:

let keyRoles: [Rule] = [Rule(rule: "First"), Rule(rule: "Second")]

After filtering 2 element array forms I should recived only first becouse both of the rules match the pattern.

The code for filtering:

forms.filter { (form) in Bool in 
   return form.roles.contains(where: { (rule) -> Bool in 
      keyRoles.contains(rule)
   })
}

Will return nothing, an empty array. Any ideas what Am I doing wrong ?

Thanks in advance!

3 Answers 3

1

Problem is you are using contains which will return if any one of the keyRole matches form role. But your expected result is all keyRoles must match.

let filtered = forms.filter { (form) -> Bool in
    for kr in keyRoles {
        if !form.roles.contains(where: { (r) -> Bool in
            return r.rule == kr.rule
        }) {
            return false
        }
    }
    return true
}

Note: if keyRoles is empty it won't filter anything (forms == filtered)

Sign up to request clarification or add additional context in comments.

Comments

1

You code has a problem, you are checking whether rule (which is a references) exists in the keyRoles array and that will always return false since keyRoles array has different references than in forms. Because you've created objects instead of reusing them, although the content is the same but they are in different memory space.

you've inserted Rule objects in the form that are different in keyRoles array

Either it can be solved if you use keyRoles as below

let keyRoles: [firstRoles, secondRoles]

Or while checking the

keyRoles.contains(rule)

You need to add predicate so that content of rule matches the content of the other rule. i.e. compare the internal data structure instead of comparing the references. Something like below

let arr = forms.filter { (form) -> Bool in
  return form.roles.contains(where: { (rule1) -> Bool in
    keyRoles.contains(where: { (rule2) -> Bool in
      rule1.rule == rule2.rule
    })
  })
}

1 Comment

Yeah, you are right about references. Now I've realized the same - How can I get the correct result while filtering via references which won't be the same, Thank you for pointing on this!
0

I would suggest a pretty good practice for your case (when using contains(where:) method), which is conforming to the Equatable Protocol:

A type that can be compared for value equality.

Note that when working with contains(where:), the element of the array should be equatables, so you could let your Rule class to be an equatable:

class Rule: Equatable {
    let rule: String

    static func ==(lhs: Rule, rhs: Rule) -> Bool {
        return lhs.rule == rhs.rule
    }

    // ... init etc.
}

Thus, you'd be able to implement:

let filteredForms = forms.filter { form -> Bool in
    for rule in keyRoles {
        return (form.roles.contains(rule)) ? true : false
    }

    return false
}

filteredForms should contains the desired result.

Obviously, by following this approach, rules instances would be "equatables":

let rule1 = Rule(rule: "First")
let rule2 = Rule(rule: "First")

rule1 == rule2 // true
rule1 != rule2 // false

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.