保留Swift中声明的字典项的顺序?

时间:2015-04-09 13:38:14

标签: swift dictionary

创建键/值字典时,它将以随机排序的形式返回。我希望它们的顺序与它的创建顺序相同。

例如,请参阅此代码:

var dict = [
    "kg": 1,
    "g": 2,
    "mg": 3,
    "lb": 4,
    "oz": 5,
    "t": 6
]

println(dict)

返回以下内容:

[kg: 1, oz: 5, g: 2, mg: 3, lb: 4, t: 6]

如何保留声明字典的顺序?

7 个答案:

答案 0 :(得分:14)

在您的情况下,自定义对象的数组可能更合适。 这是一个简单的示例,应该有助于您入门:

struct Unit : Printable {
    let name: String
    let factor: Double

    // println() should print just the unit name:
    var description: String { return name }
}


let units = [
    Unit(name: "kg", factor: 1000.0),
    Unit(name: "g", factor: 1.0),
    Unit(name: "mg", factor: 0.001),
    Unit(name: "lb", factor: 453.592292),
    Unit(name: "oz", factor: 28.349523)
]

println(units) // [kg, g, mg, lb, oz]

(我不确定非公制单位因素是否正确:)

答案 1 :(得分:5)

正如Apple所说:

  

字典是键值关联的无序集合。

链接: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html

不知道这对你有帮助,但在这个链接中有一个ordereddictionary的实现: http://www.raywenderlich.com/82572/swift-generics-tutorial

答案 2 :(得分:3)

可悲的是,Apple只在Swift中内置了三个集合数据结构。这些是数组,字典和集合。幸运的是,Apple引入了一个非常广泛且功能强大的协议层次结构,支持轻松优雅地定义您自己的集合类。

因此,如果您不介意解决方案的额外空间(可能还有时间)复杂性,您可能希望选择构建自己的Swift集合类/结构,类似于保留元素顺序的Dictionary。通过将索引与键相关联,反之亦然,将其添加到其中。有关创建自己的集合数据结构的更多信息,请参阅文档:https://developer.apple.com/documentation/swift/collection

我会给你一些帮助你的事情:

免责声明:此代码未经过测试,我在算法复杂性方面没有花费太多精力。请确定您的解决方案的要求,并自行检查以下代码是否符合。

public struct KeepOrderDictionary<Key, Value> where Key : Hashable
{
    public private(set) var values: [Value]

    fileprivate var keyToIndexMap: [Key:Int]
    fileprivate var indexToKeyMap: [Int:Key]

    public init()
    {
        self.values = [Value]()
        self.keyToIndexMap = [Key:Int]()
        self.indexToKeyMap = [Int:Key]()
    }

    public var count: Int
    {   return values.count}

    public mutating func add(key: Key, _ value: Value)
    {
        if let index = keyToIndexMap[key]
        {   values[index] = value}
        else
        {
            values.append(value)
            keyToIndexMap[key] = values.count - 1
            indexToKeyMap[values.count - 1] = key
        }
    }

    public mutating func add(index: Int, _ value: Value) -> Bool
    {
        if let key = indexToKeyMap[index]
        {
            add(key: key, value)
            return true
        }

        return false
    }

    public func get(key: Key) -> (Key, Value)?
    {
        if let index = keyToIndexMap[key]
        {   return (key, values[index])}

        return nil
    }

    public func get(index: Int) -> (Key, Value)?
    {
        if let key = indexToKeyMap[index]
        {   return (key, values[index])}

        return nil
    }

    public mutating func removeValue(forKey key: Key) -> Bool
    {
        guard let index = keyToIndexMap[key] else
        {   return false}

        values.remove(at: index)

        keyToIndexMap.removeValue(forKey: key)
        indexToKeyMap.removeValue(forKey: index)

        return true
    }

    public mutating func removeValue(at index: Int) -> Bool
    {
        guard let key = indexToKeyMap[index] else
        {   return false}

        values.remove(at: index)

        keyToIndexMap.removeValue(forKey: key)
        indexToKeyMap.removeValue(forKey: index)

        return true
    }
}

extension KeepOrderDictionary
{
    public subscript(key: Key) -> Value?
        {
        get
        {   return get(key: key)?.1}

        set
        {
            if let newValue = newValue
            {   add(key: key, newValue)}
            else
            {   let _ = removeValue(forKey: key)}
        }
    }

    public subscript(index: Int) -> Value?
        {
        get
        {   return get(index: index)?.1}

        set
        {
            if let newValue = newValue
            {   let _ = add(index: index, newValue)}
        }
    }
}

extension KeepOrderDictionary : ExpressibleByDictionaryLiteral
{
    public init(dictionaryLiteral elements: (Key, Value)...)
    {
        self.init()
        for entry in elements
        {   add(key: entry.0, entry.1)}
    }
}

extension KeepOrderDictionary : Sequence
{
    public typealias Iterator = IndexingIterator<[(key: Key, value: Value)]>

    public func makeIterator() -> KeepOrderDictionary.Iterator
    {
        var content = [(key: Key, value: Value)]()

        for i in 0 ..< count
        {
            if let value: Value = self[i], let key: Key = indexToKeyMap[i]
            {     content.append((key: key, value: value))}
        }

        return content.makeIterator()
    }
}

答案 3 :(得分:1)

可以DictionaryLiteral使用

let recordTimes: DictionaryLiteral = ["Florence Griffith-Joyner": 10.49,
                                      "Evelyn Ashford": 10.76,
                                      "Evelyn Ashford": 10.79,
                                      "Marlies Gohr": 10.81]
print(recordTimes.first!)
// Prints "("Florence Griffith-Joyner", 10.49)"

不建议使用Xcode 10.2–10.2。更改为KeyValuePairs<Key, Value>

答案 4 :(得分:1)

我遇到了相同的问题,并通过https://github.com/noman200898/OrderedDictionary/blob/master/Sources/OrderedDictionary.swift类解决了 只需将此类添加到您的项目中,然后按如下所示使用它:

    let testDic: OrderedDictionary = ["kg": 1, "g": 2, "mg": 3, "lb": 4, "oz": 5, "t": 6]
    print(testDic)
    print(testDic.unorderedDictionary)
    print(testDic.orderedKeys)
    print(testDic.orderedValues)

希望这会有所帮助。

答案 5 :(得分:0)

您可以做的一件事是对键值使用枚举而不是字符串文字。如果枚举具有Int rawValue,则可以编写一个基于原始值返回键的函数。然后,您可以使用for循环获取声明的顺序。例如

enum Months: Int{ case jan , feb, mar, apr }

let dict: Dictionary<Months, String> = [
    .jan: "One",
    .feb: "Two",
    .mar: "Three",
    .apr : "Four"
]


// This function returns key from enum rawValue.  -- requires enum as key type with Int rawValue, but works for any value type.

func keyWithRawValue(_ rawValue: Int) -> Any? {
    var key: Any?
    for element in dict {
        let rawAtElement = element.key.rawValue
        if rawValue == rawAtElement {
            key = element.key
        }
    }
    return key
}

// use this for iterating through in declared order.
for index in 0...dict.count - 1 {
    let key = keyWithRawValue(index) as! Months
    print(dict[key]! )
}

输出:

One
Two
Three
Four

答案 6 :(得分:-1)

struct MenueItem {
let title : String
let image : String}


let items = [MenueItem(title: "Readers", image: "multimedia"),
             MenueItem(title: "Live Broadcast", image: "multimedia"),
             MenueItem(title: "Quran Browsing", image: "multimedia"),
             MenueItem(title: "Personal Quran", image: "multimedia"),
             MenueItem(title: "Videos", image: "multimedia"),
             MenueItem(title: "Favorites", image: "favorites")]

...

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return items.count

...

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("MenuTableViewCell", forIndexPath: indexPath) as! MenuTableViewCell

    cell.cellTitle.text = items[indexPath.row].title
    cell.cellImage.image = UIImage(named: items[indexPath.row].image)
    return cell
}
}