Home > Mobile >  How to filter an array of objects with unique properties?
How to filter an array of objects with unique properties?

Time:05-23

Forgive the contrived example below, but how can I filter like this? Using a Set to dedup isn't an option since my real data objects have another property that is unique for each.

struct MyDataObject {
    var startDate: Date
    var endDate: Date
}

let dataObject1 = MyDataObject(startDate: Date().startOfDay(), endDate: Date().startOfDay())
let duplicateDataObject = MyDataObject(startDate: Date().startOfDay(), endDate: Date().startOfDay())

let array = [dataObject1, duplicateDataObject]

//How to filter to end up with an array of data objects with a unique start date?

CodePudding user response:

https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md

import Algorithms

array.uniqued(on: \.startDate)

Or, if you need more control of which elements get chosen:

array.uniqued(on: \.startDate) { [$0, $1].max(by: \.endDate)! }
import struct OrderedCollections.OrderedDictionary

public extension Sequence {
  @inlinable func uniqued<Subject: Hashable>(
    on projection: (Element) throws -> Subject,
    uniquingWith combine: (Element, Element) throws -> Element
  ) rethrows -> [Element] {
    try OrderedDictionary(keyed(by: projection), uniquingKeysWith: combine)
      .values
      .elements
  }

  @inlinable func max<Comparable: Swift.Comparable>(
    by getComparable: (Element) throws -> Comparable
  ) rethrows -> Element? {
    try self.max {
      try getComparable($0) < getComparable($1)
    }
  }
}
public extension Sequence {
  @inlinable func keyed<Key: Hashable>(
    by key: (Element) throws -> Key
  ) rethrows -> [KeyValuePairs<Key, Element>.Element] {
    try map { (try key($0), $0) }
  }
}

CodePudding user response:

Do you mean filter so you only keep elements that appear exactly once in the original collection?

import OrderedCollections

struct A {
    let id: String
    let thing: String
}

let things: [A] = [
    .init(id: "1", thing: "thing"),
    .init(id: "2", thing: "thing"),
    .init(id: "3", thing: "thing"),
    .init(id: "2", thing: "thing"),
    .init(id: "1", thing: "thing"),
    .init(id: "4", thing: "thing"),
    .init(id: "2", thing: "thing"),
]

let uniqueThings = OrderedDictionary<String, [A]>(grouping: things, by: \.id)
    .filter { $0.value.count == 1 }
    .values

Gives just the A's with unique id's (of 3 and 4 in this example)

Or do you mean just to deduplicate, like https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md uniqued(on:) and if so which element should be retained if there are more than one, the first seen, the last etc?

  • Related