简体   繁体   中英

How can I sort array with two kind of objects in Swift?

This is my method when I sort elements by position property. Both DBSet and DBItem have that property.

@objc(DBCategory)
class DBCategory: NSManagedObject {

    @NSManaged var identifier: String
    @NSManaged var items: Set<DBItem>
    @NSManaged var sets: Set<DBSet>

}

And this is how I use it

private var elements = [AnyObject]()

private func prepareElements() {
    elements.removeAll(keepCapacity: false)
    if let items = currentCategory?.items {
        for item in items {
            elements.append(item)
        }
    }

    if let sets = currentCategory?.sets {
        for set in sets {
            elements.append(set)
        }
    }

    elements.sort {
        var previousPosition = 0
        var currentPosition = 0
        if let set = $0 as? DBSet {
            previousPosition = Int(set.position)
        }

        if let item = $0 as? DBItem {
            previousPosition = Int(item.position)
        }

        if let set = $1 as? DBSet {
            currentPosition = Int(set.position)
        }

        if let item = $1 as? DBItem {
            currentPosition = Int(item.position)
        }

        return previousPosition < currentPosition
    }
}

position is type of Int16

How can I simplify that?

Create a protocol that defines your position property. Let's call it HasPosition. Declare an array of [HasPosition]. You can then sort the array directly with any of the standard sorting functions.

If .position returns the same type in both cases, and that type is comparable, you can simplify that to the following:

elements.sort {
    let l = ($0 as? DBSet)?.position ?? ($0 as? DBItem)?.position
    let r = ($1 as? DBSet)?.position ?? ($1 as? DBItem)?.position
    return l < r
}

The key to this working is that there is a version of < that works for optionals, where nil is defined as less than any non-nil value. So values in the array not of one of the two types will be sorted to the beginning.

When you have the array as [AnyObject] and the position is Int16 , this should works:

elements.sort {
    ($0.position as Int16?) < ($1.position as Int16?)
}

This works because AnyObject has any @objc properties as Optional .

Demo:

class DBItem:NSObject {
    var position:Int16
    init(position: Int16) { self.position = position }
    override var description:String  { return "DBItem(\(position))" }
}

class DBSet:NSObject {
    var position:Int16
    init(position: Int16) { self.position = position }
    override var description:String  { return "DBSet(\(position))" }
}

var elements:[AnyObject] = [
    DBItem(position: 5),
    DBSet(position: 2),
    DBItem(position: 42),
    DBSet(position: 62),
    DBSet(position: 21),
    DBItem(position: 6),
    DBSet(position: 36),
    DBItem(position: 24),
]


elements.sort {
    ($0.position as Int16?) < ($1.position as Int16?)
}

println(elements)
// -> [DBSet(2), DBItem(5), DBItem(6), DBSet(21), DBItem(24), DBSet(36), DBItem(42), DBSet(62)]

And your prepareElements() can be simplified as:

private func prepareElements() {
    elements = []
    if let ct = currentCategory {
        elements += Array(ct.items) as [AnyObject]
        elements += Array(ct.sets) as [AnyObject]
        elements.sort {
            ($0.position as Int16?) < ($1.position as Int16?)
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM