TransWikia.com

Group a list of json object by property inside the common array using iOS Swift

Stack Overflow Asked by vidalbenjoe on December 27, 2021

I would like to ask if how to group the object by another object inside its common array based on the id in Swift.

Here’s the JSON response, I need to group the list of the item by program id.

{
"id": "",
"ordered_by": 64,
"order_details": [
    {
        "resource": "Product",
        "required_prescription": false,
        "item": {
            "id": 6,
            "name": "Synergistic Copper Gloves",
            "code": "51537661-C",
            "enabled": true,
            "generic_name": "Mediocre Steel Wallet",
            "price_cents": 200000
        },
        "program": {
            "id": 12, <----PROGRAM ID
            "name": "Synergistic Wooden Shoes",
            "provider": "Synergistic Rubber Coat",
            "discount_type": "fixed"
        }
    },
    {
        "resource": "Product",
        "required_prescription": true,
        "item": {
            "id": 7,
            "name": "Rustic Leather Table",
            "code": "74283131-P",
            "enabled": true,
            "generic_name": "Incredible Bronze Clock",
            "price_cents": 8994
        },
        "program": {
            "id": 12, <----PROGRAM ID
            "name": "Synergistic Wooden Shoes",
            "provider": "Synergistic Rubber Coat",
            "discount_type": "fixed"
        }
    },
    {
        "resource": "Product",
        "required_prescription": false,
        "item": {
            "id": 116,
            "name": "Ergonomic Marble Hat",
            "code": "98845056-A",
            "enabled": true,
            "generic_name": "Incredible Granite Lamp",
            "price_cents": 8267
        },
        "program": {
            "id": 10, <----PROGRAM ID
            "name": "Durable Rubber Bag",
            "provider": "Aerodynamic Steel Chair",
            "discount_type": "fixed"
        }
    }
]}

For example, the item with program id 12 should be inserted under its common program.
This should be the expected object after grouping. The item was grouped by program id 12 & 10.

[
  {
    "id": 12, <----- PROGRAM ID
    "name": "Synergistic Wooden Shoes",
    "provider": "Synergistic Rubber Coat",
    "discount_type": "fixed",
    "item": [
      {
        "id": 6,
        "name": "Synergistic Copper Gloves",
        "code": "51537661-C",
        "enabled": true,
        "generic_name": "Mediocre Steel Wallet",
        "price_cents": 200000
      },
      {
        "id": 7,
        "name": "Rustic Leather Table",
        "code": "74283131-P",
        "enabled": true,
        "generic_name": "Incredible Bronze Clock",
        "price_cents": 8994
      }
    ]
  },
  {
    "id": 10, <----PROGRAM ID
    "name": "Durable Rubber Bag",
    "provider": "Aerodynamic Steel Chair",
    "discount_type": "fixed",
    "item": [
      {
        "id": 116,
        "name": "Ergonomic Marble Hat",
        "code": "98845056-A",
        "enabled": true,
        "generic_name": "Incredible Granite Lamp",
        "price_cents": 8267
      }
    ]
  }
]

I’ve successfully made it in Java using the sample code below:

private String parseJson(String source) {
    JSONArray result = new JSONArray();
    List<Integer> ids = new ArrayList<>();
    HashMap<Integer,JSONObject> programs = new HashMap<>();
    try {
        JSONObject jSource = new JSONObject(source);
        JSONArray orderDetails = jSource.getJSONArray("order_details");
        if (orderDetails.length() > 0) {
            for (int i = 0; i < orderDetails.length(); i++) {
                JSONObject jsonObject = orderDetails.getJSONObject(i);
                JSONObject item = jsonObject.getJSONObject("item");
                JSONObject program = jsonObject.getJSONObject("program");
                int programId = jsonObject.getJSONObject("program").getInt("id");
                if (!ids.contains(programId)) {
                    ids.add(programId);
                    program.put("item",new JSONArray().put(item));
                    programs.put(programId,program);
                }else{
                    program.put("item",programs.get(programId).getJSONArray("item").put(item));
                }
            }

            for(int k :programs.keySet()){
                result.put(programs.get(k));
            }

        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    return result.toString();
}

I’m trying to replicate it on swift but I’m looking for a different approach, not the same approach as I did in Java. I tried to used reduce and filter method but it doesn’t work.

Any answer would be hight appreciated.
Thanks

3 Answers

I was able to group the JSON based on its common program id by creating another model ProgramWith items.

struct ProgramWithItems: Codable {
    let id: Int?
    let name: String?
    let provider: String?
    let discountType: String?
    let resource: String?
    let discount: String?
    let item: [Item]
} 

Then I used the reduce method and populate the ProgramWithItems model using the code below:

func groupItems(_ response: CartEntity.Response) -> [ProgramWithItems] {
        response.orderDetails!.reduce(into: []) { (result: inout [ProgramWithItems], detail: OrderDetail) in
            guard let index = result.firstIndex(where: { $0.id == detail.program?.id }) else {
                
                var item = detail.item
                item?.quantity = detail.quantity
                item?.costCents = detail.cost_cents
                
                let newProgram = ProgramWithItems(
                    id: detail.program?.id,
                    name: detail.program?.name ?? "",
                    provider: detail.program?.provider ?? "",
                    discountType: detail.program?.discountType ?? "",
                    resource: detail.resource ?? "",
                    discount: detail.program?.discount,
                    item: [item!])
                result.append(newProgram)
                return
            }
            
            let existingProgram = result[index]
            
            var item = detail.item
            item?.quantity = detail.quantity
            item?.costCents = detail.cost_cents
            
            let extendedProgram = ProgramWithItems(
                id: existingProgram.id,
                name: existingProgram.name,
                provider: existingProgram.provider,
                discountType: existingProgram.discountType,
                resource: detail.resource ?? "",
                discount: detail.program?.discount,
                item: existingProgram.item + [item!])
            result[index] = extendedProgram
        }
    }

Answered by vidalbenjoe on December 27, 2021

Few comments on an answer from @omerfarukozturk,

  1. Flatmap is deprecated so you can use map or compactMap.
  2. In omerfarukozturk's answer he returns only program id while grouping which is not as per requirement, we need whole program details instead of just Id

So I have applied some other logic.

  1. Bind data with model

     let arrDetails = //Bind data with model, response.orderDetails
    
  2. Get Unique program ID

    let arrProgramID = arrDetails.compactMap{ $0.program }.removingDuplicates(byKey: .id)
    

    // removingDuplicates is logic for remove duplicate programs and have unique program array with all information

  3. Bind array in [[Program: [Item]]] form

     let finalArray = arrProgram.map { (program) -> [Program: [Item]] in
    
         let arr = arrDetails.filter{ $0.program.id == program.id }.compactMap{ $0.item }
    
         return [program: arr]
    
      } 
    
  4. Adding extension for removing duplicates

    extension Array {
         func removingDuplicates<T: Equatable>(byKey key: KeyPath<Element, T>)  -> [Element] {
             var result = [Element]()
             var seen = [T]()
             for value in self {
               let key = value[keyPath: key]
                if !seen.contains(key) {
                    seen.append(key)
                    result.append(value)
                }
            }
             return result
         }
     }
    

Answered by Bhavin Vaghela on December 27, 2021

You can use Dictionary(grouping:) to group a list. Not completely answer, but for your case you can create a logic like below;

Assume you have a decoded response model as response for your json.

let flatttenOrderDetails = response.order_details.flatMap( { $0 })
let grouped = Dictionary(grouping: flatttenOrderDetails, by: { (element: Detail) in
    return element.program.id
})

Answered by Ömer Faruk Öztürk on December 27, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP