How to group tableview cells based on field in JSON array

此生再无相见时 提交于 2019-12-31 05:42:04

问题


Essentially I have am using JSON data to create an array and form a tableview.

I would like the table cells to be grouped by one of the fields from the JSON array.

This is what the JSON data looks like:

[{"customer":"Customer1","number":"122039120},{"customer":"Customer2","number":"213121423"}]

Each number needs to be grouped by each customer.

How can this be done?

This is how I've implemented the JSON data using the table:

CustomerViewController.swift

import UIKit

class CustomerViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, FeedCustomerProtocol {

    var feedItems: NSArray = NSArray()
    var selectedStock : StockCustomer = StockCustomer()
    let tableView = UITableView()
    @IBOutlet weak var customerItemsTableView: UITableView!

    override func viewDidLoad() {

        super.viewDidLoad()



        //set delegates and initialize FeedModel
        self.tableView.allowsMultipleSelection = true
        self.tableView.allowsMultipleSelectionDuringEditing = true

        self.customerItemsTableView.delegate = self
        self.customerItemsTableView.dataSource = self

        let feedCustomer = FeedCustomer()

        feedCustomer.delegate = self
        feedCustomer.downloadItems()

            }


    }


    func itemsDownloaded(items: NSArray) {

        feedItems = items
        self.customerItemsTableView.reloadData()
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // Return the number of feed items

        print("item feed loaded")
        return feedItems.count

    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        // Retrieve cell

        let cell = tableView.dequeueReusableCell(withIdentifier: "customerGoods", for: indexPath) as? CheckableTableViewCell

        let cellIdentifier: String = "customerGoods"
        let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!

        // Get the stock to be shown
        let item: StockCustomer = feedItems[indexPath.row] as! StockCustomer
        // Configure our cell title made up of name and price


        let titleStr = [item.number].compactMap { $0 }.joined(separator: " - ")


        return myCell
    }

    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        tableView.cellForRow(at: indexPath)?.accessoryType = .none
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {


        tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark

        let cellIdentifier: String = "customerGoods"
        let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
        myCell.textLabel?.textAlignment = .left


    }

}

FeedCustomer.swift:

import Foundation

protocol FeedCustomerProtocol: class {
    func itemsDownloaded(items: NSArray)
}


class FeedCustomer: NSObject, URLSessionDataDelegate {



    weak var delegate: FeedCustomerProtocol!

    let urlPath = "https://www.example.com/example/test.php"

    func downloadItems() {

        let url: URL = URL(string: urlPath)!
        let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)

        let task = defaultSession.dataTask(with: url) { (data, response, error) in

            if error != nil {
                print("Error")
            }else {
                print("stocks downloaded")
                self.parseJSON(data!)
            }

        }

        task.resume()
    }

    func parseJSON(_ data:Data) {

        var jsonResult = NSArray()

        do{
            jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray

        } catch let error as NSError {
            print(error)

        }

        var jsonElement = NSDictionary()
        let stocks = NSMutableArray()

        for i in 0 ..< jsonResult.count
        {

            jsonElement = jsonResult[i] as! NSDictionary

            let stock = StockCustomer()

            //the following insures none of the JsonElement values are nil through optional binding
            if let number = jsonElement[“number”] as? String,
                let customer = jsonElement["customer"] as? String,

            {

                stock.customer = customer
                stock.number = number
            }

            stocks.add(stock)

        }

        DispatchQueue.main.async(execute: { () -> Void in

            self.delegate.itemsDownloaded(items: stocks)

        })
    }
}

StockCustomer.swift:

import UIKit

class StockCustomer: NSObject {

    //properties of a stock

    var customer: String?
    var number: String?


    //empty constructor

    override init()
    {

    }

    //construct with @name and @price parameters

    init(customer: String) {

        self.customer = customer



    }



    override var description: String {
        return "Number: \(String(describing: number)), customer: \(String(describing: customer))"

    }

}

回答1:


You can achieve this by making an array of array. So something like this

[[{"customer": "customer1", "number": "123"}, {"customer": "customer1", "number": "456"}], [{"customer": "customer2", "number": "678"}, {"customer": "customer2", "number": "890"}]]

This is not the only data structure you can use to group. Another possibility is:

{"customer1": [{"customer": "customer1", "number": "123"}, {"customer": "customer1", "number": "456"}], "customer2": [{"customer": "customer2", "number": "678"}, {"customer": "customer2", "number": "890"}]}

Then you can use UITableView sections to group by customers. Section count would be the number of inside arrays and each section would contain as many rows as there are numbers in that inside array.




回答2:


You can group a sequence based on a particular key using one of the Dictionary initializer,

init(grouping:by:)

The above method init will group the given sequence based on the key you'll provide in its closure.

Also, for parsing such kind of JSON, you can easily use Codable instead of manually doing all the work.

So, for that first make StockCustomer conform to Codable protocol.

class StockCustomer: Codable {
    var customer: String?
    var number: String?
}

Next you can parse the array like:

func parseJSON(data: Data) {
    do {
        let items = try JSONDecoder().decode([StockCustomer].self, from: data)
        //Grouping the data based on customer
        let groupedDict = Dictionary(grouping: items) { $0.customer } //groupedDict is of type - [String? : [StockCustomer]]
        self.feedItems = Array(groupedDict.values)
    } catch {
        print(error.localizedDescription)
    }
}

Read about init(grouping:by:) in detail here: https://developer.apple.com/documentation/swift/dictionary/3127163-init

Make the feedItems object in CustomerViewController of type [[StockCustomer]]

Now, you can implement UITableViewDataSource methods as:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.feedItems.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "customerGoods", for: indexPath) as! CheckableTableViewCell
    let items = self.feedItems[indexPath.row]
    cell.textLabel?.text = items.compactMap({$0.number}).joined(separator: " - ")
    //Configure the cell as per your requirement
    return cell
}

Try implementing the approach with all the bits and pieces and let me know in case you face any issues.



来源:https://stackoverflow.com/questions/56142037/how-to-group-tableview-cells-based-on-field-in-json-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!