2

I want to filter an array of objects.

struct Person {
   let name: String
}

let p1 = Person(name:"test1")
let p2 = Person(name:"test1")
let p3 = Person(name:"test2")

let persons = [p1, p2, p3]

How i can filter the persons list and return the persons which have the same name?

I have tried to use a filter method, but I can't apply it with multiple arguments.

I am looking for a functional solution like a filter or a reduce function and not looping over the list.

6
  • What do you mean by this: I have tried to use a filter method, but I can't apply it with multiple arguments.? Commented Jul 17, 2016 at 14:28
  • 1
    What do you mean with filter the persons list and return the persons which have the same name? With this example input [Person(name:"test1"), Person(name:"test2")] what result do you expect? Commented Jul 17, 2016 at 14:32
  • I am looking for the Haskell groupBy function in swift :) Commented Jul 17, 2016 at 14:33
  • @samir: Ok, let me know if my answer if what you are looking for Commented Jul 17, 2016 at 14:38
  • 1
    To make this Q&A useful for future SO readers: consider updating the question with more details as to what you're actually trying to achieve (in the body of the function, not just as comments: e.g. include the Haskell groupBy comparison and an example "before" and "after operation" state). In it's current form it just seems as if let personsWithTest1Name = persons.filter { $0.name == "test1" } would do the trick, but judging from the answers below (and your feedback to them( this is not what you wish for. Commented Jul 17, 2016 at 14:50

2 Answers 2

1

Since you are looking for a groupBy functionality here's my solution

let personsByName = persons.reduce([String:[Person]]()) { (res, person) -> [String:[Person]] in
    var res = res
    res[person.name] = (res[person.name] ?? []) + [person]
    return res
}

Now personsByName is a dictionary where the key is the name of the person and the values are all the Person struct with that name

["test2": [Person(name: "test2")], "test1": [Person(name: "test1"), Person(name: "test1")]]

If you want the result as [[Person]]

let personsLists = Array(
        persons.reduce([String:[Person]]()) { (res, person) -> [String:[Person]] in
            var res = res
            res[person.name] = (res[person.name] ?? []) + [person]
            return res
         }
     .values)

[[Person(name: "test2")], [Person(name: "test1"), Person(name: "test1")]]]

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

2 Comments

Thanks. How you will do it if you want to return [[Person]] ?
@samir: please check it out my update
0

Some details are not specified, so this may not be what you want...

func retrieveDuplicates(persons: [Person]) -> [[Person]] {
    let duplicates = persons.reduce([:] as [String: [Person]], combine: {
        var dict = $0
        dict[$1.name] = (dict[$1.name] ?? []) + [$1]
        return dict
    }).map{$1}.filter{$0.count > 1}
    return duplicates
}
let duplicates = retrieveDuplicates(persons)
print(duplicates) //->[[Person(name: "test1"), Person(name: "test1")]]

let p4 = Person(name:"test3")
let p5 = Person(name:"test3")
let p6 = Person(name:"test3")
let p7 = Person(name:"test1")

let morePersons = [p1, p2, p3, p4, p5, p6, p7]

let moreDuplicates = retrieveDuplicates(morePersons)
print(moreDuplicates) //->[[Person(name: "test3"), Person(name: "test3"), Person(name: "test3")], [Person(name: "test1"), Person(name: "test1"), Person(name: "test1")]]

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.