AaplMike
AaplMike

Reputation: 353

Sort dictionary using ordered array

I have a dictionary which has a key I'd like to do multiple sort on. One key, however, can't be sorted alphabetically or it would not make sense. Those keys can have only a defined set of values, and I'm trying to sort using a specified order.

The second sort is easy, so I can use the sort descriptor way, which works fine:

let costSort = NSSortDescriptor( key: "cmc", ascending: false )
let sortDescriptors = [costSort]
let sortedCards = cards.sortedArrayUsingDescriptors( sortDescriptors )

The other key must be ordered in particular order specified by an array:

let rarity = ["Special", "Mythic rare", "Rare", "Uncommon", "Common", "Basic Land"]

So, not in alphabetical order at all, yet I need to make sure that the sort follows this ordering. I'm unsure how to code it.

Example records:

let cards = [
    ["name": "card1", "rarity": "Common", "cmc": 2],
    ["name": "card2", "rarity": "Rare", "cmc": 4],
    ["name": "card3", "rarity": "Common", "cmc": 1],
    ["name": "card4", "rarity": "Mythic rare", "cmc": 8]
]

The goal is that the sorted result, using [raritySort,costSort] would be:

card4
card2
card1
card3

Upvotes: 1

Views: 160

Answers (3)

Alberto Barrera
Alberto Barrera

Reputation: 329

What about this?

let sortedCards = cards.sorted { (d1, d2) -> Bool in
    d1.valueForKey("cmc") as Int > d2.valueForKey("cmc") as Int
}

Ok, what about this?

var cards = [
    ["name": "card1", "rarity": "Common", "cmc": 2],
    ["name": "card2", "rarity": "Rare", "cmc": 4],
    ["name": "card3", "rarity": "Common", "cmc": 1],
    ["name": "card4", "rarity": "Mythic rare", "cmc": 8]
]

let rarity = ["Special": 1, "Mythic rare": 2, "Rare": 3, "Uncommon": 4, "Common": 5, "Basic Land": 6]

let sortedCards = cards.sorted { (d1, d2) -> Bool in

    let d1R: Int = rarity[d1.valueForKey("rarity") as String]!
    let d2R: Int = rarity[d2.valueForKey("rarity") as String]!
    if d1R == d2R {
        return d1.valueForKey("cmc") as Int > d2.valueForKey("cmc") as Int
    } else {
        return d1R < d2R
    }

}

Upvotes: 0

Connor
Connor

Reputation: 64644

Here's a possible solution. Is uses Swift's sorted method and a closure instead of an NSSortDescriptor.

var cards = [
    ["name": "card1", "rarity": "Common", "cmc": 2],
    ["name": "card2", "rarity": "Rare", "cmc": 4],
    ["name": "card3", "rarity": "Common", "cmc": 1],
    ["name": "card4", "rarity": "Mythic rare", "cmc": 8]
]
let rarity = ["Special", "Mythic rare", "Rare", "Uncommon", "Common", "Basic Land"]

var sortedCards = sorted(cards) {
    find(rarity, $0["rarity"] as String) > find(rarity, $1["rarity"] as String) || $0["cmc"] as Int >= $1["cmc"] as Int
}
println(sortedCards)

I used the more concise closure syntax here. You could also expand it like this:

var sortedCards = sorted(cards, { (d1, d2) -> Bool in
    if find(rarity, d1["rarity"] as String) > find(rarity, d2["rarity"] as String){
        return true
    }
    return d1["cmc"] as Int >= d2["cmc"] as Int
})

Upvotes: 2

Craig Otis
Craig Otis

Reputation: 32054

If you want to use the NSArray sort methods, you can do something like this using an NSSortDescriptor based on a custom NSComparator that looks up the indexes of the rarities in your array, and uses them to determine relative rank/ordering:

let rarities = ["Special", "Mythic rare", "Rare", "Uncommon", "Common", "Basic Land"]

let cards = [
    ["name": "card1", "rarity": "Common", "cmc": 2],
    ["name": "card2", "rarity": "Rare", "cmc": 4],
    ["name": "card3", "rarity": "Common", "cmc": 1],
    ["name": "card5", "rarity": "Common", "cmc": 7],
    ["name": "card6", "rarity": "Common", "cmc": 0],
    ["name": "card4", "rarity": "Mythic rare", "cmc": 8]
]

let nsarrayCards = cards as NSArray

let costSort = NSSortDescriptor( key: "cmc", ascending: false )

let raritySort = NSSortDescriptor(key: "rarity", ascending: false, comparator: { (leftRarity, rightRarity) -> NSComparisonResult in
    if let leftRarityIdx = find(rarities, leftRarity as String) {
        if let rightRarityIdx = find(rarities, rightRarity as String) {
            if rightRarityIdx == leftRarityIdx {
                return .OrderedSame
            }
            return leftRarityIdx > rightRarityIdx ? .OrderedAscending : .OrderedDescending
        }
    }
    return .OrderedSame
})


let sortDescriptors = [raritySort, costSort]
let sortedCards = nsarrayCards.sortedArrayUsingDescriptors( sortDescriptors )

At the end here, sortedCards contains:

[{
    cmc = 8;
    name = card4;
    rarity = "Mythic rare";
}, {
    cmc = 4;
    name = card2;
    rarity = Rare;
}, {
    cmc = 7;
    name = card5;
    rarity = Common;
}, {
    cmc = 2;
    name = card1;
    rarity = Common;
}, {
    cmc = 1;
    name = card3;
    rarity = Common;
}, {
    cmc = 0;
    name = card6;
    rarity = Common;
}]

Upvotes: 2

Related Questions