MartynE23
MartynE23

Reputation: 59

type 'any' has no subscript members error with map

Ive tried for hours with every possible combination and can't get this to work.

my function:

func allItems() -> [TodoItem] {
    let todoDictionary = UserDefaults.standard.dictionary(forKey: ITEMS_KEY) ?? [:]
    let items = Array(todoDictionary.values)

    return items.map({TodoItem(
        deadline: $0["deadline"] as! Date,
        title: $0["title"] as! String, 
        UUID: $0["UUID"] as! String!,
        description: $0["description"] as! String,
        imageSavePath: ($0["imageSavePath"] as! String),
        isItComplete: $0["isItComplete"] as! Bool
    )}).sorted(by: {(left: TodoItem, right:TodoItem) -> Bool in
        (left.deadline.compare(right.deadline) == .orderedAscending)
    })
}

Gives me the error 'type 'Any' has no subscript members. I have researched and tried to alter:

func allItems() -> [TodoItem]

to

func allItems() -> [Any:[TodoItem]]

However this raises the error that 'Type Any does not conform to Hashable protocol'. Is this the right lines to go down?

Please help.....before my macbook goes out the window!

Upvotes: 1

Views: 434

Answers (2)

OOPer
OOPer

Reputation: 47886

Take care of the types of the variables.

dictionary(forKey:) returns [String: Any], so todoDictionary is a [String: Any].

(In older Swift, Value type of generic dictionary was imported as AnyObject, but now it's Any.)

You are retrieving only values and making it to an Array, so items is an Array<Any>.

Then you are applying map to items, so $0 in it is an Any.

Thus, you cannot apply any methods including subscript to Any.


If you need to apply $0["deadline"] or something and the values may differ, the type of $0 needs to be a Dictionary with its Key type String and its Value type mixed, which is represented by Any in Swift 3.

The type $0 needs to be [String: Any], so the type of items needs to be Array<[String: Any]> (aka [[String: Any]]).

Which means the Value type of todoDictionary needs to be [String: Any], its Key type is already constrained to String, so the type of todoDictionary needs to be [String: [String: Any]].


Conclusion, change the first line of your allItems() to this:

let todoDictionary = UserDefaults.standard.dictionary(forKey: ITEMS_KEY) as? [String: [String: Any]] ?? [:]

Upvotes: 2

Alexander
Alexander

Reputation: 63271

You're taking only the values out of the dictionary. You're then trying to subscript these Any instances with Strings, which won't work because the compiler has no idea if those Any instances are guaranteed to be Dictionaries or anything else that can be subscripted by a String.

Your question is quite lacking and doesn't convert what you're actually trying to achieve. I suspect you're trying to map a [String: [String: Any]] to a [String: TodoItem], here are your issues:

func allItems() -> [TodoItem] {
    let todoDictionary = UserDefaults.standard.dictionary(forKey: ITEMS_KEY) ?? [:] //inferred type: [String: Any]
    let items = Array(todoDictionary.values) //inferred type [Any]

    return items.map({TodoItem(
        deadline: $0["deadline"] as! Date, //[Any] can only be subscripted by indices, not by string.
        title: $0["title"] as! String, 
        UUID: $0["UUID"] as! String!,
        description: $0["description"] as! String,
        imageSavePath: ($0["imageSavePath"] as! String),
        isItComplete: $0["isItComplete"] as! Bool
    )}).sorted(by: {(left: TodoItem, right:TodoItem) -> Bool in
        (left.deadline.compare(right.deadline) == .orderedAscending)
    })
}

Upvotes: 0

Related Questions