根据与特定属性对应的另一个数组中的值对对象数组进行排序

时间:2016-01-21 20:10:25

标签: ios arrays swift sorting

假设从JSON API检索对象数组:

[
    {
        "id": 48,
        "name": "Bob"
    },
    {
        "id": 198,
        "name": "Dave"
    },
    {
        "id": 2301,
        "name": "Amy"
    },
    {
        "id": 990,
        "name": "Colette"
    }
]

// e.g. for ease of reproduction:

let dataObjects = [
    ["id": 48, "name": "Bob"], 
    ["id": 198, "name": "Dave"], 
    ["id": 2301, "name": "Amy"], 
    ["id": 990, "name": "Colette"]
]

在客户端上,我想允许用户重新排序这些对象。为了保存订单,我将在数组中存储一个id列表:

let index: [Int] = [48, 990, 2103, 198]

如何根据排序索引中的id顺序重新排序原始数据对象数组?

dataObjects.sort({ 
    // magic happens here maybe?
}

所以最后,我得到了:

print(dataObjects)
/* 
[
    ["id": 48, "name": "Bob"], 
    ["id": 990, "name": "Colette"], 
    ["id": 2301, "name": "Amy"], 
    ["id": 198, "name": "Dave"]
]
/*

3 个答案:

答案 0 :(得分:2)

方法A)将数据解析为一个简单的字典,其中字典的键是用于对其进行排序的id:

func sort<Value>(a: [Int: Value], basedOn b: [Int]) -> [(Int, Value)] {
    return a.sort { x, y in
        b.indexOf(x.0) < b.indexOf(y.0)
    }
}

// ....
let a = [48:"Bob", 198:"Dave", 2301:"Amy", 990:"Colette"]
let b = [48, 198, 2301, 990]
sort(a, basedOn: b)

方法B)使用某种自定义DataObject类型:(可能是最佳方式)

struct DataObject {
    let id: Int
    let name: String

    init(_ id: Int, _ name: String) {
        self.id = id
        self.name = name
    }
}

func sort(a: [DataObject], basedOn b: [Int]) -> [DataObject] {
    return a.sort { x, y in
        b.indexOf(x.id) < b.indexOf(y.id)
    }
}


let a = [DataObject(48, "Bob"), DataObject(198, "Dave"), DataObject(2301, "Amy"), DataObject(990, "Colette")]
let b = [48, 198, 2301, 990]

sort(a, basedOn: b)
/* [
     DataObject(id: 48, name: "Bob"), 
     DataObject(id: 990, name: "Colette"), 
     DataObject(id: 198, name: "Dave"), 
     DataObject(id: 2301, name: "Amy")
] */

方法C)使用原始 json值,可以用较少“干净”的方式完成:

func sort<Value>(a: [[String: Value]], basedOn b: [Int]) -> [[String: Value]] {
    return a.sort { x, y in
        let xId = x["id"] as! Int
        let yId = y["id"] as! Int
        return b.indexOf(xId) < b.indexOf(yId)
    }
}

let dataObjects = [
    ["id": 48, "name": "Bob"],
    ["id": 198, "name": "Dave"],
    ["id": 2301, "name": "Amy"],
    ["id": 990, "name": "Colette"]
]

let b = [48, 198, 2301, 990]

sort(dataObjects, basedOn: b)

答案 1 :(得分:2)

您可以从Swift所具有的功能支持中受益,并使用map函数迭代index数组并获取相应的对象:

let sortedObjects = index.map{ id in
    dataObjects.filter{ $0["id"] as? Int == id }.first
}

这假设在映射数据对象之前正确计算了index数组。

修改

感谢user3441734提出的flatMap建议。这是一个使用它的解决方案,基本上它只是将map替换为flatMap,以便摆脱映射数组中可能的nil值,并将结果数组转换为一系列非选项。

let sortedObjects = index.flatMap{ id in
    dataObjects.filter{ $0["id"] as? Int == id }.first
}

如果空index导致保留相同的顺序,那么我们可以简单地测试一下:

let sortedObjects = index.count > 0 ? index.flatMap{ id in
    dataObjects.filter{ $0["id"] as? Int == id }.first
} : dataObjects

答案 2 :(得分:1)

您不需要使用您可以按所需对象排序的索引来保留数组,让我们说您具有以下结构:

struct DataObject {
   var id: Int
   var name: String
}

var data = [DataObject]()
data.append(DataObject(id: 48, name: "Bob"))
data.append(DataObject(id: 198, name: "Dave"))
data.append(DataObject(id: 2301, name: "Amy"))
data.append(DataObject(id: 990, name: "Colette"))

您可以使用sort函数对结果进行排序并将结果返回到其他数组中,如果您想将其放在原地,则只需sorted函数

// ordered by id
var sortedData = data.sort { index.indexOf($0.id) < index.indexOf($1.id) }

我希望这对你有所帮助。