0

Total noob question, but I've done my best and can't figure out how to iterate over my array and determine if it contains all the values of another array.

Here is my array class (dailyJobs):

class JobsAndHabits {

var name: String
var multiplier: Double
var assigned: String
var order: Int

init(jobName: String, jobMultiplier: Double, jobAssign: String, jobOrder: Int) {

    self.name = jobName
    self.multiplier = jobMultiplier
    self.assigned = jobAssign
    self.order = jobOrder
}

And here is the class I want to check (users):

class UserClass {

var imageURL: String
var firstName: String
var birthday: Int
var passcode: Int
var gender: String
var childParent: String

init(userProfileImageURL: String, userFirstName: String, userBirthday: Int, userPasscode: Int, userGender: String, isUserChildOrParent: String) {

    self.imageURL = userProfileImageURL
    self.firstName = userFirstName
    self.birthday = userBirthday
    self.passcode = userPasscode
    self.gender = userGender
    self.childParent = isUserChildOrParent

}

Here is my JSON tree if that helps:

{
"-Kw2SkmAqo3G_cVFP26Y" : {
"assigned" : "Aiden",
"multiplier" : 1,
"name" : "table",
"order" : 2
},
"-Kw2SkmAqo3G_cVFP26Z" : {
"assigned" : "Mom",
"multiplier" : 1,
"name" : "dishes",
"order" : 3
},
"-Kw2SyhJNkjKNQ-sKBeT" : {
"assigned" : "Sophie",
"multiplier" : 1,
"name" : "floors",
"order" : 0
},
"-Kw2SyhJNkjKNQ-sKBeU" : {
"assigned" : "Savannah",
"multiplier" : 1,
"name" : "laundry",
"order" : 1
},
"-Kw7H3pY2L5PzLLdd6qD" : {
"assigned" : "Dad",
"multiplier" : 1,
"name" : "another daily test",
"order" : 4
},
"-Kw7HAWwAvv5ZjYuNOrg" : {
"assigned" : "Sophie",
"multiplier" : 1,
"name" : "daily job testsaroo!",
"order" : 5 }

And here is what I've tried:

Attempt #1:

var tempArray = [String]()
    for user in self.users {
        for job in self.dailyJobs {
            if job.assigned == user.firstName {
                print(user.firstName,"has a job",job.name)
                tempArray.append(user.firstName)
            }
        }
    }
    for user in self.users {
        if tempArray.contains(user.firstName) {
            print("true")
        } else {
            print("false")
        }
    }

Attempt #2: (this didn't even compile)

for job in self.dailyJobs {
        if self.users.contains(where: job) {
            print(true)
        }
    }

Attempt #3: prints "Failed" repeatedly.

for job in self.dailyJobs {
        for user in self.users {
            if job.assigned != user.firstName {
                print("FAILED")
            }
        }
    }

How do I check if each of the users in my users array has at least one job assigned? In other words, how do I make sure that inside the dailyJobs array every user.firstName is represented at least once?

I'm thinking I have to do some sort of loop, but I'm not sure how to proceed. Can someone point me in the right direction? Right now I'm just going in circles (if you'll pardon the pun).

Phontaine Judd
  • 428
  • 7
  • 17
  • Note that you should only use a `class` for those rare cases it makes sense to use references rather than values. For most data objects you should use `struct`. Then you can get rid of those `init` methods entirely because `structs` synthesize an `init` that sets all your properties. Also, in Swift 4 if you adhere to the `Codable` protocol you automatically get encoding and decoding from JSON (and possibly other data structures). –  Oct 11 '17 at 01:49
  • Excellent info. Would I be able to swap out my classes for structs on a 1:1 ratio, or would it require a complete rewrite of my code? What I mean is, could I go in and just change my class to a struct and everything would be fine, or does it require a lot more work because classes and structs aren't comparable objects? – Phontaine Judd Oct 11 '17 at 02:03
  • Unless you are using some language features that are highly class-specific you should have little problem swapping the two. –  Oct 11 '17 at 02:04
  • Some further reading on why use one over the other: https://stackoverflow.com/questions/24232799/why-choose-struct-over-class/24232845 and https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html –  Oct 11 '17 at 02:05
  • I read the apple documentation when I first started out, and it made it seem like a `class` was the better way to go. So now I'm confused, because the link you sent me made me think that `structs` are the way to go. I'm going to have to do some more research, it appears. – Phontaine Judd Oct 11 '17 at 02:18
  • FWIW, my app gets data from Firebase and does stuff with it. I was looking to only have to download my data once per session in some sort of global variable. Is that what `structs` do? If so, that would be great! Because right now I'm calling the Firebase to retrieve data with every new view. – Phontaine Judd Oct 11 '17 at 02:20
  • Do `structs` store data, or are they just a blueprint for data, like `classes`? – Phontaine Judd Oct 11 '17 at 02:21
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/156406/discussion-between-colgraff-and-phontaine-judd). –  Oct 11 '17 at 02:21
  • Just wanted to update you. I changed over all my classes to structs. So far so good. I think I'm liking the change. It simplified my code, that's for sure. Thanks for the suggestion. – Phontaine Judd Oct 14 '17 at 04:14

4 Answers4

1

Attempt #1 is close. You can only say it's true after checking all users:

func areAllAssigned(users: [UserClass], tempArray: [String]) -> Bool {
  for user in users {
    if !tempArray.contains(user.firstName) {
        return false
    }
  }
  return true
}

In fact, you don't need the outer for loop:

for job in self.dailyJobs {
    print(job.assigned, "has a job", job.name)
    tempArray.append(job.assigned)
}

areAllAssigned(users: self.users, tempArray: tempArray)
aaron
  • 39,695
  • 6
  • 46
  • 102
  • I figured it out on my own right when you answered. Thank you for your help. I'll mark your answer as correct, because that's exactly what I ended up doing. – Phontaine Judd Oct 11 '17 at 01:22
  • Great! Thanks for the mark :) Note that, as mentioned in my answer, you don't need a nested `for` loop to create `tempArray`. – aaron Oct 11 '17 at 01:30
1

Make the class to conform Hashable and use Set. You don't need that much code.

Updated

You don't need to use Hashable here. I misunderstood your question. I simplified the code but basically it has what you need.

struct JobsAndHabits {
    let assigned: String
}

struct UserClass {
    let firstName: String
}

let users = [UserClass(firstName: "person_a"),
             UserClass(firstName: "person_b"),
             UserClass(firstName: "person_c"),
             UserClass(firstName: "person_d")]

let jobs = [JobsAndHabits(assigned: "person_a"),
            JobsAndHabits(assigned: "person_c")]

let names = Set(users.map({ return $0.firstName }))
let assigned = Set(jobs.map({ return $0.assigned }))

print(names.subtracting(assigned)) // users who doesn't have jobs
Ryan
  • 4,799
  • 1
  • 29
  • 56
  • I've read a little about conforming to `Hashable` values and also about using `set`, but seeing as that I'm a noob, can you please elaborate how you would use them? I'm totally up for cleaning up and simplifying my code. – Phontaine Judd Oct 11 '17 at 04:17
1

Here's how to do it with sets (without making the classes Hashable).

if Set(users.map{firstName}).isSubset(of:dailyJobs.map{$0.assigned})
{
  // all users are assigned to at least one job
}
else
{
  let employed = Set(dailyJobs.map{$0.assigned})
  let jobless  = users.filter{ !employed.contains($0.firstName) }
}
Alain T.
  • 40,517
  • 4
  • 31
  • 51
  • If you use ‘filter’ no need to use Set. – Ryan Oct 11 '17 at 23:48
  • Depends on the number of jobs and users, a simple filter will give O(n/2) while the set will provide O(1) + setup time. In any case, my objective here was to show a few examples of the set related functions (not to provide the best solution for reacting to unemployed users) – Alain T. Oct 12 '17 at 00:06
0

After finding this answer, I got it to work.

My code:

func checkIfAllUsersHaveDailyJobs() -> Bool {
    var tempArray = [String]()
    for job in self.dailyJobs {
        for user in self.users {
            if job.assigned == user.firstName {
                print(user.firstName,"has the job:",job.assigned)
                tempArray.append(user.firstName)
            }
        }
    }
    print(tempArray.count)

    for user in self.users {
        if !tempArray.contains(user.firstName) {
            return false
        }
    }
    return true
}
Phontaine Judd
  • 428
  • 7
  • 17