Сохранять порядок элементов словаря, как указано в Swift?

При создании словаря ключ/значение он возвращается как случайный порядок сортировки. Я надеялся, что они будут в том порядке, в каком они были созданы.

Например, см. Этот код:

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]

Как сохранить порядок, в котором был объявлен словарь?

Ответ 1

В вашем случае может быть более подходящим массив пользовательских объектов. Вот простой пример, который поможет вам начать:

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]

(Я не уверен, правильны ли неметрические единичные коэффициенты :)

Ответ 2

Как говорит Apple:

Словари - это неупорядоченные коллекции ассоциаций с ключом.

Ссылка: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html

Не знаю, поможет ли это вам, но в этой ссылке есть реализация упорядоченного словаря: http://www.raywenderlich.com/82572/swift-generics-tutorial

Ответ 3

К сожалению, Apple только построила три коллекции данных в Swift из коробки. Это Массив, Словарь и Набор. К счастью, Apple представила очень обширную и мощную иерархию протоколов, которая помогает легко и элегантно определять ваши собственные классы коллекций.

Итак, если вы не возражаете против дополнительной space- (и, возможно, time-) сложности вашего решения, вы можете захотеть создать свой собственный класс/структуру коллекции Swift, который напоминает словарь, который сохраняет порядок, в котором элементы были добавляется к нему, связывая индексы с ключами и наоборот. Дополнительную информацию о создании собственных коллекционных таблиц данных см. В документации: 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()
    }
}

Ответ 4

Swift 5.1:

Используйте экземпляр KeyValuePairs, когда вам нужна упорядоченная коллекция пар ключ-значение, и вам не требуется быстрый поиск ключа, который обеспечивает тип Dictionary.

Вы инициализируете экземпляр KeyValuePairs с помощью литерала Swift-словаря. Помимо поддержания порядка исходного литерала словаря, KeyValuePairs также позволяет дублировать ключи. Например:

let recordTimes: KeyValuePairs = ["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)"

Ответ 5

Я решил решить эту проблему следующим образом:

  1. Я добавил в свой объект свой собственный массив словаря со строковыми значениями

    разделы var: Словарь> = Словарь()

    var orderedKeys: Array = Array()

  2. При добавлении элемента в словарь, но "Ключ" еще не создан (1-й элемент с таким ключом) Я добавляю ключ в упорядоченныеKeys

    if sections[sectionName] != nil {
        sections[sectionName]?.append(item)
    } else { // 1st item with such key
        sections[sectionName] = [item]
        orderedKeys.append(sectionName)
    }
    
  3. Тогда, когда мне нужно что-то получить из этого Словаря, я использую мои упорядоченныеKeys вместо default allKeys

    func getSectionById(sectionId : Int) {
        let sectionName = orderedKeys[sectionId]
        return (selectedPlaylist?.sections[sectionName])!
    }
    

Но, вероятно, если применимо для вашего случая, лучшим решением является создание Array с помощью Structs:

var mySections : Array<Section> = Array()

Struct Section {
  let title: String
  let objects: Array
}

Ответ 6

Одна вещь, которую вы можете сделать, это использовать перечисление для ключевых значений вместо строковых литералов. Если перечисление имеет Int rawValue, вы можете написать функцию, которая возвращает ключ на основе необработанного значения. Затем вы можете использовать for-loop для получения заявленного порядка. Например

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

Ответ 7

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
}
}