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.

beyowulf
  • 15,101
  • 2
  • 34
  • 40
samir
  • 4,501
  • 6
  • 49
  • 76
  • What do you mean by this: `I have tried to use a filter method, but I can't apply it with multiple arguments.`? – xoudini Jul 17 '16 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? – Luca Angeletti Jul 17 '16 at 14:32
  • I am looking for the Haskell groupBy function in swift :) – samir Jul 17 '16 at 14:33
  • @samir: Ok, let me know if my answer if what you are looking for – Luca Angeletti Jul 17 '16 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. – dfrib Jul 17 '16 at 14:50
  • Finally, for future reference: if you're looking for Haskell-like functional methods that are seemingly missing in Swift, have a look at the content of excellent [SwiftSequence](https://github.com/oisdk/SwiftSequence) by oisdk (who happens to be the answerer of the forementioned duplicate thread). – dfrib Jul 17 '16 at 15:03

2 Answers2

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")]]]

Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
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")]]
OOPer
  • 47,149
  • 6
  • 107
  • 142