Home > Net >  how can I parse a multidimensional array into UITableview with sections
how can I parse a multidimensional array into UITableview with sections

Time:07-07

I have a remote db call delivering a list of items that looks something like this

{
   "result":[
      {
         "Section":"Client",
         "result":[
            {
               "Owner":"Client Owned",
               "Barcode":"10000473",
               "Quantity":"5",
               "Description":"Large Dog",
               "Status":"ok"
            },
            {
               "Owner":"Client Owned",
               "Barcode":"10000439",
               "Quantity":"1",
               "Description":"Privacy Wall Panels - 4ft Green Countertop 4CT-G",
               "Status":"missing"
            }
         ]
      },
      {
         "Section":"Rental",
         "result":[
            {
               "Owner":"Rental",
               "Barcode":"10000006",
               "Quantity":"1",
               "Description":"2X2. X42 BLK CABINET ",
               "Status":"missing"
            },
            {
               "Owner":"Rental",
               "Barcode":"10000007",
               "Quantity":"2",
               "Description":"TALL BLK TABLETOP BASE",
               "Status":"missing"
            },
            {
               "Owner":"Rental",
               "Barcode":"10000005",
               "Quantity":"1",
               "Description":"TALL BLK TABLETOP BASE",
               "Status":"missing"
            },
            {
               "Owner":"Rental",
               "Barcode":"",
               "Quantity":"3",
               "Description":"Asbestos Board",
               "Status":"missing"
            }
         ]
      }
   ]
}

I want to parse it into a UITableview with 2 sections, Rental and Client. My current struct setup is not working.

struct ReceiveIn: Codable {  
   var Section: String  
   var result: [Components]    
}

struct Components: Codable {
    var Owner: String
    var Barcode: String
    var Quantity: String
    var Description: String
    var Status: String
}

I would like to parse the db data into a tableview with two sections: client and rental, which is determined by the array I'm currently sending with the api, OR I could use the Owner var in the component. Whichever is easier. Not sure how to change my structs to make either work so any help appreciated.

Decoding code below

func parse(_ data: Data){
    
    let decoder = JSONDecoder()
    
    if let jsonUnits = try? decoder.decode(ReceiveIn.self, from: data) {
        ReviewList = jsonUnits.result
        
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
}

CodePudding user response:

It's hard to offer a good solution without knowing more about the json structure. Here's a very simple example that makes many assumptions about the structure of the json. A more robust / safe solution definitely exists but requires more information about the potential json schema (e.g. will there ever be multiple "client" or "rental" sections? are there other possible sections? etc).

The following works for example posted in question. Comment if you'd like me to expand on the solution.

struct Sections: Decodable {
    let clientSection: Section?
    let rentalSection: Section?
    
    enum CodingKeys: String, CodingKey {
        case result
    }
    
    init(from decoder: Decoder) throws {
        let container = try? decoder.container(keyedBy: CodingKeys.self)
        let sections = try? container?.decode([Section].self, forKey: .result)
        clientSection = sections?.first { $0.section == "Client" }
        rentalSection = sections?.first { $0.section == "Rental" }
    }
}

struct Section: Decodable {
    let section: String
    let components: [Component]
    
    enum CodingKeys: String, CodingKey {
        case section = "Section"
        case components = "result"
    }
}

struct Component: Decodable {
    let Owner: String
    let Barcode: String
    let Quantity: String
    let Description: String
    let Status: String
}

CodePudding user response:

Several issues here:

  • Your structs are wrong it should be:

    // MARK: - Response
      struct Response: Codable {
          let result: [ResponseResult]
      }
    
      // MARK: - ResponseResult
      struct ResponseResult: Codable {
          let section: String
          let result: [Result]
    
          enum CodingKeys: String, CodingKey {
              case section = "Section"
              case result
          }
      }
    
      // MARK: - Result
      struct Result: Codable {
          let owner, barcode, quantity, resultDescription: String
          let status: String
    
          enum CodingKeys: String, CodingKey {
              case owner = "Owner"
              case barcode = "Barcode"
              case quantity = "Quantity"
              case resultDescription = "Description"
              case status = "Status"
          }
      }
    
  • You are ignoring all errors while decoding. This is the least amount of do/catch you should be doing:

    func parse(_ data: Data){
    
          let decoder = JSONDecoder()
    
          do {
              let jsonUnits = try decoder.decode(ReceiveIn.self, from: data)
              ReviewList = jsonUnits.result
          } catch {
              print(error)
          }
    
          DispatchQueue.main.async {
              self.tableView.reloadData()
          }
    
      }
    

With that out of the way:

Regarding your initial question

how can I parse a multidimensional array into UITableview with sections.

This is not an multidimensional array. It´s a custom object with an array of another custom object.

One strategy to display this in to 2 sections would be to get your values from the approriate array. This controller implementation should get you going:

class Viewcontroller: UITableViewController{
    //Store the `ResponseResult`s from api here
    var responses: [ResponseResult] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        //The number of sections would be the number of ResponseResults
        responses.count
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        //The number of rows should be the number of `Result` in that `ResponseResult`
        responses[section].result.count
    }
    
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        //The header for that section can be extracted by getting the section property of the respective `ResponseResult`
        responses[section].section
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //Get the `Result` object for the appropriate row
        let result = responses[indexPath.section].result[indexPath.row]
        
        // do the rest
    }
    
    func parse(_ data: Data){
        
        let decoder = JSONDecoder()
        // !Important! never user try?
        do {
            //Decode the response to the top level construct `Response` and store that
            responses = (try decoder.decode(Response.self, from: data)).result
        } catch {
            print(error)
        }
            
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
        
    }

}

This approach would assure that you can use as many section ( "Rental" / "Client") as you may like. Every single one will get its own Section in tableview.

  • Related