Search code examples
arraysswiftgenericsgeneric-programmingswift-protocols

How to generic a function where params are different structs with different properties?


Please refer the following code:

import UIKit

struct Item {
    var brandId = 1
    var name: String = ""
}
struct Store {
    var areaName = ""
    var name: String = ""
}

let itemArray = [Item(brandId: 1, name: "item1"), Item(brandId: 2, name: "item2"), Item(brandId: 1, name: "item3") ]

let storeArray = [Store(areaName: "hk", name: "store1"), Store(areaName: "bj", name: "store2"), Store(areaName: "hk", name: "store3")]


var intKeys = [Int]()
var groupedItems = [[Item]]()

var stringKeys = [String]()
var groupedStores = [[Store]]()

extension Array {

    func transTo2d() -> [[Element]] {
        let grouped = [[Element]]()

        return grouped
    }

}

itemArray.forEach { (item) in
    let brandId = item.brandId
    if !intKeys.contains(brandId) {
        intKeys.append(brandId)
        var newArray = [Item]()
        newArray.append(item)
        groupedItems.append(newArray)
    } else {
        let index = intKeys.index(of: brandId)!
        groupedItems[index].append(item)
    }
}

My final goal is could using itemArray.transTo2d() get a 2d array based on item's brandId, using storeArray.transTo2d() get a 2d array based on store's areaName. I don't how to generic the function that trans 1d array to a 2d array based on the key?


Solution

  • I don't think you can write a generic extension for an Array where the elements will either be of type Item or Store since both of them don't share any relation for you to write a common generic method. You can write extensions for Array where the elements will be of the mentioned type. You just need to conform both of your structs to the equatable protocol.

    struct Item {
        var brandId = 1
        var name: String = ""
    }
    
    extension Item : Equatable{
        static func ==(lhs: Item, rhs: Item) -> Bool{
            return lhs.brandId == rhs.brandId
        }
    }
    
    struct Store {
        var areaName = ""
        var name: String = ""
    }
    
    extension Store : Equatable{
        static func ==(lhs: Store, rhs: Store) -> Bool{
            return lhs.areaName == rhs.areaName
        }
    }
    
    extension Array where Element == Store{
        func transform()->[[Store]]{
            var storeArray = self
            var groupedArray = [[Store]]()
            while storeArray.count > 0{
                if let firstElement = storeArray.first{
                    groupedArray.append(storeArray.filter{$0.areaName == firstElement.areaName})
                    storeArray = storeArray.filter{$0.areaName != firstElement.areaName}
                }
            }
            return groupedArray
        }
    }
    
    extension Array where Element == Item{
        func transform()->[[Item]]{
            var itemArray = self
            var groupedArray = [[Item]]()
            while itemArray.count > 0{
                if let firstElement = itemArray.first{
                    groupedArray.append(itemArray.filter{$0.brandId == firstElement.brandId})
                    itemArray = itemArray.filter{$0.brandId != firstElement.brandId}
                }
            }
            return groupedArray
        }
    }
    

    Using the transform function

    let storeArray = [Store(areaName: "hk", name: "store1"), Store(areaName: "bj", name: "store2"), Store(areaName: "hk", name: "store3")]
    let itemArray = [Item(brandId: 1, name: "item1"), Item(brandId: 2, name: "item2"), Item(brandId: 1, name: "item3") ]
    print(storeArray.transform())
    print(itemArray.transform())
    

    This will print this output which is what I believe you wanted.

    [[Store(areaName: "hk", name: "store1"), Store(areaName: "hk", name: "store3")], [Store(areaName: "bj", name: "store2")]]
    [[Item(brandId: 1, name: "item1"), Item(brandId: 1, name: "item3")], [Item(brandId: 2, name: "item2")]]