0

I have one array "users" with all user data and and second array "userIds" having user's id. I have to fetch User from "users" array using "userIds" array

struct User {
    let name: String
    let id: Int
}

let users: [User] = [User(name: "a", id: 1), 
                     User(name: "b", id: 2), 
                     User(name: "c", id: 3), 
                     User(name: "d", id: 4), 
                     User(name: "d", id: 5)]

let userIds = [2,3,2,5]

result array that I want is :

[User(name: "b", id: 2),
 User(name: "c", id: 3),
 User(name: "b", id: 2),
 User(name: "d", id: 5)]

so it can have duplicate data according to the data in "userIds".

Now I tried using Higher order function filter:

let result = users.filter { (user) -> Bool in
    return userIds.contains(user.id)
}

but this removes the duplicate data and the output is :

[User(name: "b", id: 2),
 User(name: "c", id: 3),
 User(name: "d", id: 5)]

One approach that I tried is using for loop :

var result = [User]()

for i in userIds {
    result.append(users.filter({ $0.id == i }).first!)
}

which gives the desired output but if there is a better approach please suggest.

4
  • A loop looks like a good solution to me. This isn’t about filtering for me but rather to fetch from a source (array1) using a request (array2) Commented Mar 15, 2020 at 7:57
  • Thank you for your suggestion @JoakimDanielson. Please look at the answer that I have added. Commented Mar 15, 2020 at 9:00
  • You should probably use structs instead of dicts, for array1. Otherwise you're just going to be in a bottomless pit of force casts, type errors and crashes. Commented Mar 15, 2020 at 14:04
  • @Alexander-ReinstateMonica: it's struct only in the project. To ask question I created a sample one in playground. Commented Mar 15, 2020 at 15:14

2 Answers 2

1

You can solve this using first(where:) to search through users:

let result = userIds.compactMap { desiredDataValue in
    users.first(where: { $0.id == desiredDataValue })
}

But if you're doing this a lot, it would probably speed things up if you built a datastructure that allows for fast lookup by the "id" value. You should compare the performance for yourself, and see if you do this enough/frequently enough for it to be worthwhile:

let dictsByData = Dictionary(uniqueKeysWithValues:
    users
        .lazy
        .map { dict in
            (key: dict.id, value: dict)
        }
)

let result = userIds.compactMap { desiredDataValue in dictsByData[desiredDataValue]! }
result.forEach { print($0) }
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks a ton buddy. I loved the idea of creating datastructure, it's fast. Except one thing, I am using compactMap instead of map.
@Amit I wouldn't advise to do that. I know that compactMap might feel like you're "properly handling optionals", because you don't see any !. But really what you're doing is turning a loud noticeable failure (a crash) into a subtle silent failure (elements missing from your result). There might be times when that's appropriate, but I think it's better to either force unwrap (if you're sure array1 should always have matches for the values in array2), or leave the nils in place and handle them later.
yes you are right. However array1 always matches for the values in array2. But still I will check for both scenarios before integrating in the project. Thanks again buddy.
One more thing, Do you know the reason of downvote for the question ?
@Amit Generally it's a bad idea to name things like "userArray". The "arrayness" cold just be conveyed by plurality: users. And something like userDict could be called usersById, which shows the exact relationship between the keys and values
|
0

Well after digging few more and with the help of this blog:

I tried doing like this:

let results = userIds.compactMap { (int) -> User? in
    var matchedUser: User?
    if users.contains(where: { (user) -> Bool in
        if user.id == int {
            matchedUser = user
        }
        return user.id == int
    }) {
        return matchedUser
    }
    return nil
}

and in playground I checked the count the code was executed :

enter image description here

and it seems like the count is less comparing to "for" loop.

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.