-2

I have an array of Objects with this structure:

struct Message {
    let name: String
    let fromNumber: String
    let toNumber: String
}

let array = [Message(name: "John", fromNumber: "123456789", toNumber: "987654321")
            ,Message(name: "John", fromNumber: "987654321", toNumber: "123456789")
            ,Message(name: "Smith", fromNumber: "11223344", toNumber: "987654321")
            ,Message(name: "Smith", fromNumber: "987654321", toNumber: "11223344")]

How to group them and get an array with unique values? note that name property is not unique.

The result should be:

[Message(name: "John", fromNumber: "123456789", toNumber: "987654321"),
 Message(name: "Smith", fromNumber: "11223344", toNumber: "987654321")]
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
Scar
  • 3,460
  • 3
  • 26
  • 51
  • 2
    Define what's "unicity" first. How do we know which value to keep? – Larme Jan 04 '21 at 09:05
  • @Larme The `fromNumber` and `toNumber` should be from the same users. This is to group a chat messages. – Scar Jan 04 '21 at 09:06
  • I mean between `Message(name: "Smith", fromNumber: "11223344", toNumber: "987654321")` and `Message(name: "Smith", fromNumber: "987654321", toNumber: "11223344")` I don't understand why keep only the first one. Because from/to is the "same" (by reverse order allowed), and it's the first item on the list? – Larme Jan 04 '21 at 09:08
  • @Larme The order is not guaranteed, and i need to display one message from the same conversation – Scar Jan 04 '21 at 09:10
  • It's unclear. You want in fact keep only the value that have `toNumber` equals to the target one? Then do you need another operation? Because currently, it's just `array.filter{ $0.toNumber == targetNumber }` – Larme Jan 04 '21 at 09:13
  • possible duplicate of https://stackoverflow.com/a/34712330/2303865 – Leo Dabus Jan 04 '21 at 17:24

2 Answers2

1

My understanding is that any of the messages with the same name and number combinations might be returned so for this solution I am making use of Set and Hashable.

First make the struct conform to Hashable in such way that we compare both of the number properties as one.

extension Message: Hashable {
    static func == (lhs: Message, rhs: Message) -> Bool {
            return lhs.name == rhs.name &&
                Set(arrayLiteral: lhs.fromNumber, lhs.toNumber) == Set(arrayLiteral: rhs.fromNumber, rhs.toNumber)
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(name)
        hasher.combine(Set(arrayLiteral: fromNumber, toNumber))
    }
}

and then use the familiar solution with converting the array to a set and then back to an array

let unique = Array(Set(array))
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
-1

Possible solution

func unique(with data: [Message]) -> [Message] {
    if data.isEmpty { return [] }
    var arrUnique: [Message] = [data[0]]
    
    for message in data.dropFirst() {
        
        if !arrUnique.contains(where: { (msg) -> Bool in
            (msg.fromNumber == message.fromNumber || msg.toNumber == message.toNumber || msg.fromNumber == message.toNumber || msg.toNumber == message.fromNumber) && message.name == msg.name
        }) {
            arrUnique.append(message)
        }
    }
    return arrUnique
}

enter image description here

Raja Kishan
  • 16,767
  • 2
  • 26
  • 52
  • Not my downvote but this is extremely inefficient. You should use a set. https://stackoverflow.com/a/34712330/2303865 – Leo Dabus Jan 04 '21 at 17:21