1

I have Dictionary which contains String keys and Array of Objects as value. These values are added from the sorted Array of objects into Dictionary using append method. The values are categorized into keys based on the first letter of object property. But returns unsorted Dictionary.

The dictionaries are declared:

var namesDic = [String: [Name]]()

var filteredNames = [String: [Name]]()

And iterating through array and appending into Dictionary:

for name in names {
        let letterIndex = name.getName().index(name.getName().startIndex, offsetBy: 0)

        let letter = name.getName()[letterIndex]

        if namesDic[String(letter)] != nil {
            namesDic[String(letter)]?.append(name)
        } else {
            namesDic[String(letter)] = [name]
        }
    }
    filteredNames = namesDic

}

Name structure:

struct Name {
    var id: Int!
    var name: String!
    var native: String!
    var meaning: String!
    var origin: String!
    var isFavorite: Bool
    var gender: String!

    init(id: Int, name: String, native: String, meaning: String, origin: String, isFavorite: Int, gender: String) {
        self.id = id
        self.name = name
        self.native = native
        self.meaning = meaning
        self.origin = origin
        if isFavorite == 0 {
            self.isFavorite = false
        } else { self.isFavorite = true }
        self.gender = gender
    }
}

I found in debugging that they are unsorted when they are appended to dictionary. I understand sort on Swift Dictionary is not working but I want a work around to sort Dictionary by key to pass it to TableView.

I went through many questions/answers here but they are all for [String: String] not Array of Objects.

19
  • stackoverflow.com/a/46940363/2303865 If your names array input it is unsorted just sort it before creating your dictionary. Btw you should post your Name object structure/class Commented Nov 7, 2017 at 1:59
  • @LeoDabus I get the following error after sorting and assigning: Cannot assign value of type '[(key: String, value: [Name])]' to type '[String : [Name]]' Commented Nov 7, 2017 at 2:06
  • post your Name structure/class declaration Commented Nov 7, 2017 at 2:07
  • 1
    All var, all implicitly unwrapped optionals. That's a huge red flag. Commented Nov 7, 2017 at 2:44
  • 2
    Note that the types of some of your properties can be improved here – gender should almost certainly be an enumeration, and id probably shouldn't be an Int (does it make sense to add two ids together? should a name ID be the same type as other ID types in your program?). Rob's advise here is great – create a nested ID type. Commented Nov 7, 2017 at 11:34

2 Answers 2

4
struct Name: CustomStringConvertible {
    let id: Int
    let name: String
    let native: String
    let meaning: String
    let origin: String
    let isFavorite: Bool
    let gender: String
    var description: String {
        return "Id: " + String(id) + " - Name: " + name 
    }
}

let name1 = Name(id: 1, name: "Tim Cook", native: "native", meaning: "meaning", origin: "origin", isFavorite: true, gender: "Male")
let name2 = Name(id: 2, name: "Steve Jobs", native: "native", meaning: "meaning", origin: "origin", isFavorite: true, gender: "Male")
let name3 = Name(id: 3, name: "Tiger Woods", native: "native", meaning: "meaning", origin: "origin", isFavorite: true, gender: "Male")
let name4 = Name(id: 4, name: "Socrates", native: "native", meaning: "meaning", origin: "origin", isFavorite: true, gender: "Male")

let names = [name1, name2, name3, name4]


let dictionary = names.sorted(by: {$0.name < $1.name }).reduce(into: [String: [Name]]()) { result, element in
    // make sure there is at least one letter in your string else return
    guard let first = element.name.first else { return }
    // create a string with that initial
    let initial = String(first)
    // initialize an array with one element or add another element to the existing value
    result[initial, default: []].append(element)
}

let sorted = dictionary.sorted {$0.key < $1.key}
print(sorted)   // "[(key: "S", value: [Id: 4 - Name: Socrates, Id: 2 - Name: Steve Jobs]), (key: "T", value: [Id: 3 - Name: Tiger Woods, Id: 1 - Name: Tim Cook])]\n"
Sign up to request clarification or add additional context in comments.

11 Comments

Note you can say result[initial, default: []].append(element) instead of result[initial] = (result[initial] ?? []) + [element] :) Also see my above comment.
@LeoDabus No worries :) If you're interested – subscript(_:default:) will actually still cause a copy of the underlying array each time append(_:) is called on it (you could optimise by removing and appending), but I'm trying to get that changed such that the underlying array can be mutated directly through subscript(_:default:).
@LeoDabus You were introducing a new generic placeholder K which shadowed the Key placeholder, additionally you need to constrain Value to being a RangeReplaceableCollection (so you can append), and you cannot use an array literal, as it may not be ExpressibleByArrayLiteral. Take a look at swift.sandbox.bluemix.net/#/repl/5a01e7ad9ce6876bc6920dc4
@LeoDabus A more generalised version of efficiently mutating a dictionary value could look like this: swift.sandbox.bluemix.net/#/repl/5a01e9449ce6876bc6920dc6. Although this will be redundant once subscript(_:default:) (hopefully!) supports direct mutation via an addressor.
|
2

According to Apple's documentation

A dictionary stores associations between keys of the same type and values of the same type in a collection with no defined ordering. Each value is associated with a unique key, which acts as an identifier for that value within the dictionary. Unlike items in an array, items in a dictionary do not have a specified order. You use a dictionary when you need to look up values based on their identifier, in much the same way that a real-world dictionary is used to look up the definition for a particular word.

Further information is available on Apple's Website

Workaround

One thing that could be done is to create an array of sorted keys and then use that array to access the dictionary values

4 Comments

Thanks for the answers: I understand sort on Swift Dictionary is not working but I want a work around to sort Dictionary by key to pass it to TableView.
Updated the answer
Is that clear enough or do you want more clarification?
It is clear but I am still working to fix the problem. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.