通过对象属性获取数组中不同的元素

问题描述 投票:0回答:6

我有一个对象数组。 我想通过根据名称属性比较对象来获取该数组中的不同元素

class Item {
var name: String
init(name: String) {
    self.name = name
}
}
let items = [Item(name:"1"), Item(name:"2"), Item(name:"1"), Item(name:"1"),Item(name:"3"), Item(name:"4")]

结果:

let items = [Item(name:"1"), Item(name:"2"),Item(name:"3"), Item(name:"4")]

我怎样才能快速做到这一点?

arrays swift distinct-values
6个回答
38
投票

这是一个数组扩展,用于根据给定键返回唯一的对象列表:

extension Array {
    func unique<T:Hashable>(by: ((Element) -> (T)))  -> [Element] {
        var set = Set<T>() //the unique list kept in a Set for fast retrieval
        var arrayOrdered = [Element]() //keeping the unique list of elements but ordered
        for value in self {
            if !set.contains(by(value)) {
                set.insert(by(value))
                arrayOrdered.append(value)
            }
        }

        return arrayOrdered
    }
}

对于您的示例,您可以这样做:

let uniqueBasedOnName = items.unique{$0.name}

4
投票

希望这对您有帮助:

class Item:Equatable, Hashable {
    var name: String
    init(name: String) {
        self.name = name
    }
    var hashValue: Int{
      return name.hashValue
    }

}

func ==(lhs: Item, rhs: Item) -> Bool {
    return lhs.name == rhs.name
}


let items = [Item(name:"1"), Item(name:"2"), Item(name:"1"), Item(name:"1"),Item(name:"3"), Item(name:"4")]

var uniqueArray = Array(Set(items))

1
投票

在 Swift 中,您可以使用 Equatable 协议来区分对象数组中的唯一元素。

 struct Item:Equatable{
        var name:String
        var price:Double

        init(name:String,price:Double) {
            self.name = name
            self.price = price
        }

        static func ==(lhs: Item, rhs: Item) -> Bool{
            return lhs.name == rhs.name
        }
    }

    class ViewController: UIViewController {

       var books = [Item]()
        override func viewDidLoad() {
            super.viewDidLoad()
            items.append(Item(name: "Example 1", price: 250.0))
            items.append(Item(name: "Example 2", price: 150.0))
            items.append(Item(name: "Example 1", price: 150.0))
            items.append(Item(name: "Example 1", price: 150.0))
            items.append(Item(name: "Example 3", price: 100.0))
            items.unique().forEach { (item) in
                print(item.name)
            }
        }

    }

    extension Sequence where Iterator.Element: Equatable {
        func unique() -> [Iterator.Element] {
            return reduce([], { collection, element in collection.contains(element) ? collection : collection + [element] })
        }
    }

1
投票

我使用了@Ciprian Rarau 的甜蜜答案,然后意识到如果它们不是唯一的,我什至不需要首先添加元素。所以我为此写了一个小扩展(受到答案的启发)。

extension Array {
    public mutating func appendIfUnique<T: Hashable>(_ newElement: Element, check property: ((Element) -> (T))) {
        for element in self {
            if property(element) == property(newElement) { return }
        }
        
        append(newElement)
    }
}

仅在元素唯一时追加元素:

array.appendIfUnique(newElement, check: { $0.name })

1
投票

使用键路径而不是闭包的解决方案:

extension Sequence {
    func uniqued<Type: Hashable>(by keyPath: KeyPath<Element, Type>) -> [Element] {
        var set = Set<Type>()
        return filter { set.insert($0[keyPath: keyPath]).inserted }
    }
}

示例

struct Mock { var uuid: UUID; var num: Int }

let uuid = UUID()
let arr = [
    Mock(uuid: uuid, num: 1),
    Mock(uuid: uuid, num: 2),
    Mock(uuid: UUID(), num: 3)
]

let unique = arr.uniqued(by: \.uuid)

unique
数组将包含第一个 (num = 1) 和最后一个 (num = 3) 元素。


0
投票

替代@Ciprian Rarau的优秀答案。

此实现在复制元素之前保留所需的容量。如果大多数元素最终出现在新数组中,这会提高速度。

extension Array {
    func unique<T: Hashable>(by clause: (Element) -> T) -> Self {
        return Array(unsafeUninitializedCapacity: self.count) { buffer, initializedCount in
            var seen = Set<T>()
            for element in self {
                guard seen.insert(clause(element)).inserted else { continue }
                buffer.initializeElement(at: initializedCount, to: element)
                initializedCount += 1
            }
        }
    }
}

可以将其与 KeyPaths 一起使用,如下所示:

employees.unique(by: \.taxID)

或者通过“常规”lambda:

address.unique { "\($0.zipCode)\($0.houseNumber)" }

© www.soinside.com 2019 - 2024. All rights reserved.