0

The goal of this code below is to filter out dictionaries with a certain ID, where ID is a string.

    let dictArray = networkData["dicts"] as! [[String:AnyObject]]
    localData["dicts"] = dictArray.filter{ ($0["id"] as! String) != sample.getId() }

This code, however, generates an error:

Cannot invoke 'filter' with an argument list of type '(([String : AnyObject]) throws -> Bool)'

Based on other SO answers like this one and this one, it seems the error is the dictionaries don't conform to Equatable.

So is the only option for using filter to create a custom class to hold the array of dictionaries and make that class conform to Equatable?

If so, perhaps it seems cleaner to simply iterate and create a new array.

Community
  • 1
  • 1
Crashalot
  • 33,605
  • 61
  • 269
  • 439
  • What are *hash objects*? – Alexander Jan 03 '17 at 02:57
  • Please make sure you define `filteredData: [String: [[String: AnyObject]]]` – bubuxu Jan 03 '17 at 03:04
  • @Alexander He probably means dictionaries – matt Jan 03 '17 at 03:09
  • @matt That's what I suspected, but he's not comparing equality of dictionaries anywhere – Alexander Jan 03 '17 at 03:11
  • I would suggest that declare `let id = sample.getId()` out of the filter block and use the id instead of. – bubuxu Jan 03 '17 at 03:16
  • @Alexander sorry, dictionaries. :) exactly, no dictionary compares -- only strings. so not sure why the compiler is complaining. any clues? – Crashalot Jan 03 '17 at 04:45
  • @Crashalot What type is `.getId()`? – Alexander Jan 03 '17 at 05:51
  • @Alexander String – Crashalot Jan 03 '17 at 05:52
  • @Crashalot What type is `filteredData`? – Alexander Jan 03 '17 at 05:54
  • @Alexander `filteredData` is `[String:AnyObject]` – Crashalot Jan 03 '17 at 06:49
  • @Crashalot Welp, there's your issue. Filtering `[[String:AnyObject]]` (a.k.a. `Array>`) results in another `[[String:AnyObject]]`. You're trying to assign this to a var of type `[String:AnyObject]` (a.k.a. `Dictionary`) – Alexander Jan 03 '17 at 06:51
  • @Alexander duh. hmmm wonder why this error didn't occur before converting to Swift 3 syntax. will try this right now. meanwhile, could you post as an answer? – Crashalot Jan 03 '17 at 07:26
  • @Alexander but if `filteredData` is [String:AnyObject] doesn't that mean `filteredData["hashes"]` is `AnyObject`, which should accept `[[String:AnyObject]`? in other words, shouldn't it work since you're assigning not to `[String:AnyObject]` but `AnyObject.` – Crashalot Jan 03 '17 at 07:27
  • Array's aren't assignable to `AnyObject`. They're structs. – Alexander Jan 03 '17 at 20:52
  • @Alexander thanks so that's the issue? So how should `filteredData` be declared such that `filteredData["hashes"]` can accept an array of dictionaries? also could you kindly post as an answer? this problem only surfaced in swift 3 so the knowledge transfer here could benefit other new swift coders. – Crashalot Jan 03 '17 at 21:01
  • Does `filteredData` (Horrible name, by the way. What kind of data? what's it do?) have a static set of keys? – Alexander Jan 03 '17 at 21:16
  • @Alexander `filteredData` is a scrubbed name only for SO to make debugging simpler, at least that was the goal. :) yes, it has a static set of keys. – Crashalot Jan 03 '17 at 22:37
  • Oh okay, So if it has a static set of keys, why is it a dictionary at all? Make it into a struct or a class. – Alexander Jan 03 '17 at 22:43
  • @Alexander good question :) need to revisit the reason why. meanwhile could you at least post part of this as an answer so you can receive credit (and for the benefit of future readers)? – Crashalot Jan 03 '17 at 23:30

2 Answers2

2

Filtering [[String:AnyObject]] (a.k.a. Array>) results in another [[String:AnyObject]]. You're trying to assign this to a var of type AnyObject, which is not allowed in Swift 3, since arrays are structs, not objects.

Make a type-safe struct or object to hold this data, rather than a dict.

For example:

let dictArray = networkData["dicts"] as! [[String:AnyObject]]
let filteredDicts = dictArray.filter{ ($0["id"] as! String) != sample.getId() }         
localData["dicts"] = filteredDicts
Crashalot
  • 33,605
  • 61
  • 269
  • 439
Alexander
  • 59,041
  • 12
  • 98
  • 151
  • thanks! but to be precise we're assigning the array of dictionaries to a var of type `AnyObject` (which also does not work in swift). – Crashalot Jan 04 '17 at 00:10
  • @Crashalot Good point. You can augment my answer. Please accept it if it helped – Alexander Jan 04 '17 at 00:11
  • already upvoted. only haven't accepted because still need to fix the code. :) don't worry you will get the points if this leads to the solution. :) – Crashalot Jan 04 '17 at 00:14
  • btw any insight as to why this worked in swift 2.x and not swift 3.x? would also be valuable commentary. – Crashalot Jan 04 '17 at 00:15
  • Swift 2 allowed implicit bridging to Foundation types, such as from `Array` to `NSArray` (which is a class). This bridging has been made explicit in Swift 3, to lessen the dependence on Foundation types. – Alexander Jan 04 '17 at 00:16
  • Modified your answer to incorporate our final solution, as well as the minor correction on the source of the problem. Thanks again for your help! – Crashalot Jan 04 '17 at 02:29
  • BTW you don't happen to know the answer to this do you? http://stackoverflow.com/questions/41433100/uinavigationcontroller-show-embedded-view-controllers-with-different-orientatio – Crashalot Jan 04 '17 at 02:55
1

The issue isn't on hash objects not conform to Equatable because you are using String to do the comparing.

I have the code runs well in Playground:

// make sure data is type of [String: [[String: AnyObject]]]
// make sure filteredData is type of [String: [[String: AnyObject]]]

let key = "hashes"
if let hashArray = data[key] {
    let id = sample.getId() // make sure it's String type
    filteredData[key] = hashArray.filter { ($0["id"] as? String) != id }
}
bubuxu
  • 2,187
  • 18
  • 23