0

I got this code:

let arrData = [["id": "1",
                        "name": "Apple",
                        "category": "Fruit"],
                       ["id": "2",
                        "name": "Pie",
                        "category": "Fruit"],
                       ["id": "3",
                        "name": "Tomato",
                        "category": "Vegetable"]]

        let categorieNames = Array(Set(arrData.map({$0["category"]!})))
        var arrResult:[[String]] = []
        for i in 0..<categorieNames.count {

            let categories = arrData.filter({$0["category"] == categorieNames[i]}).map({$0["name"]!})
            arrResult.append(categories)
        }
        print("result : \(arrResult)")

it works perfectly with an Array. But now I get my data from a json:

[{"id":"1",
  "name":"Apple",
  "category":"Fruits"},
{"id":"2",
 "name":"Pie",
 "category":"Fruits"},
{"id":"3",
 "name":"Tomato",
 "category":"Vegetable"}]

here is my struct and my decode function:

struct MarketStruct : Decodable {
    let id                    : Int?
    let name                  : String?
    let category              : String?
}



  class MarketsCollectionViewController: UICollectionViewController, NetworkDelegate {

    var myMarkets : [MarketStruct]?
    var categorieNames : [Any] = []
    var categorieArray:[[String]] = []


    func didFinish(result: Data) {
        do {
            self.myMarkets  =  try JSONDecoder().decode([MarketStruct].self, from: result)
                categorieNames = Array(Set(myMarkets!.map({ ["category": $0] })))
            for i in 0..<categorieNames.count {
                let categories = myMarkets!.filter({$0["category"] == categorieNames[i]}).map({["name": $0]!})
                categorieArray.append(categories)
            }
        } catch let jsonErr {
            print("Error:", jsonErr)
        }
                self.myCollectionView.reloadData()
    }

I got an error at the filter part: Type 'MarketStruct' has no subscript members

what must I change that the above Array code works with my JSON array?

thanks for your help.

edit my current code:

 self.myMarkets = try JSONDecoder().decode([MarketStruct].self, from: result)
        categorieNames = Array(Set(myMarkets.map({$0.category })))

            for i in 0..<categorieNames.count {
                    let categories = myMarkets!.filter({$0.category == categorieNames[i]}).map({$0.name})
                    categorieArray.append(categories as! [String])
        }

        } catch let jsonErr {
            print("Error:", jsonErr)
        }
        self.myCollectionView.reloadData()

it compiles with error: Command failed due to signal: Segmentation fault: 11

if I comment categorieNames = Array(Set(myMarkets.map({$0.category }))) out it compiles without errors

2
  • myMarkets!.filter({$0.category... instead? Because $0 is now a MarketStruct, so to access category, you just have to do myMarketStruct.category. But when you did previously let categories = arrData.filter({$0["category"], $0 was a Dict, so to access the category, you need to do ["category"] (that's a subscript). Commented Jan 22, 2018 at 15:20
  • Do you have an error in console, because according to your struct, id is a Int, but in the JSON you gave, it's a String. You should get an error. Else, categorieNames2 = Array(Set(myMarkets.flatMap({$0.category}))) could fix your issue (because you have optionals stuff I think). and the first time you did map({$0["category"]!} not map({["category":$0]!} that's not the same type of data and doesn't match with you declaration, and after you do categorieNames[i], which you expect to be a String. Commented Jan 22, 2018 at 16:42

2 Answers 2

1

You are getting the compiler error because the category property is optional and therefore does not conform to Hashable.

Set requires that all elements conform to that.

One way to get around that is to use flatMap instead of map, which has the added capability of filtering out the nil values.

categorieNames = Array(Set(myMarkets.flatMap({$0.category })))

Sign up to request clarification or add additional context in comments.

Comments

0

There are a lot of issues in the code

  • In the JSON the type of id is String not Int
  • The members in the struct are supposed to be non-optional.
  • The categorieNames array is supposed to be specific [String], not unspecified [Any]
  • In the filter line access the category by dot notation, not key subscription (Error 1)
  • Rather than declaring myMarkets as optional, initialize it as an empty array (Error 2)
  • Don't use ugly index-based for loop, use fast enumeration (not really an issue)

And why do you map myMarkets to an array of dictionaries?


struct MarketStruct : Decodable {
    let id                    : String
    let name                  : String
    let category              : String
}

var myMarkets = [MarketStruct]()
var categorieNames = [String]()
var categorieArray = [[String]]()

let jsonString = """
[{"id":"1", "name":"Apple", "category":"Fruits"},
 {"id":"2", "name":"Pie", "category":"Fruits"},
 {"id":"3", "name":"Tomato", "category":"Vegetable"}]
""" 

let data = Data(jsonString.utf8)

do {
    myMarkets = try JSONDecoder().decode([MarketStruct].self, from: data)
    let categorieNames = Array(Set(myMarkets.map { $0.category }))

    for categoryName in categorieNames {
        let categories = myMarkets.filter({$0.category == categoryName}).map({$0.name})
        categorieArray.append(categories)
    }
    print(categorieArray)

} catch { print(error) }

5 Comments

Thanks for your detailed answer :) no i know the issues. But what so mean that i map myMarkets a dictionary?
myMarkets!.map({ ["category": $0] } maps the array of MarketStruct to an array of dictionaries.
Hmm i found this on the internet. I will Check tomorrow if i need this. Thanks for your help
I need the categorieNames as sectionNames in UiCollectionView
That's a new question

Your Answer

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