1

I'm looking for a way to count how many times the same product is added to an order (I'm new to Swift)

struct Product {
  var name: String
  var price: Double
}

struct Order {
  var productsSold: [Product] = []
}

currentOrder = Order()
currentOrder.productsSold.append(products[1]) //From a JSON object "products"
currentOrder.productsSold.append(products[1])
currentOrder.productsSold.append(products[2])
currentOrder.productsSold.append(products[4])

I want to have to following result but I can't find a way to do it:

currentOrder.list() --> product_1 x2 // product_2 x1 // product_4 x1

I can only list all the products with:

//Inside struc Order
func listProductsSold() {
  if productsSold.count>0 {
    for product in productsSold {
      product.printProduct()
    }
  }
}
//Inside struc Product
func printProduct() {
    print("Nom: \(name), price: \(price), couleur: \(color)")
}

And then:

currentOrder.listProductsSold()

//Which give:
//Nom: Menu cheese, price: 17.0, couleur: rgb(247, 171, 56)
//Nom: Menu cheese, price: 17.0, couleur: rgb(247, 171, 56)
//Nom: Rouge n1, price: 18.0, couleur: rgb(166, 77, 121)
//Nom: Vin vigneron, price: 22.0, couleur: rgb(166, 77, 121)
Jack G.
  • 3,681
  • 4
  • 20
  • 24
shlagwuk
  • 93
  • 11
  • You could use counted set, example from Hacking with swift: var spaceships = ["Serenity", "Nostromo", "Enterprise"] spaceships += ["Voyager", "Serenity", "Star Destroyer"] spaceships += ["Galactica", "Sulaco", "Minbari"] let countedSet = NSCountedSet(array: spaceships) print(countedSet.count(for: "Serenity")) // 2 print(countedSet.count(for: "Sulaco")) // 1 – Mago Nicolas Palacios Jun 13 '17 at 18:28

2 Answers2

2

You first need a "group by" function to collect all identical products into an array. An example of such function can be found in this answer

public extension Sequence {
    func group<U: Hashable>(by key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] {
        var categories: [U: [Iterator.Element]] = [:]
        for element in self {
            let key = key(element)
            if case nil = categories[key]?.append(element) {
                categories[key] = [element]
            }
        }
        return categories
    }
}

Then you can do something like this:

let orderSummary = currentOrder.productsSold
                        .group  { $0.name }
                        .sorted { $0.key < $1.key  }
                        .map    { "\($0.key) x \($0.value.count)" }
                        .joined(separator: ", ")

What it does:

  • .group groups the productSolds into a dictionary, whose key is the product name and value is an array of products having the same name.
  • .sorted sorts the collection of key-value pairs by the key (i.e. the product name)
  • .map builds a summary string for each product, in the template of product_name x count
  • .joined concatenates these strings into an overall order summary.
Code Different
  • 90,614
  • 16
  • 144
  • 163
1

As a simple solution to count the number of products sold per order, using a dictionary with the product name and a count:

        var productsCount = [String: Int]()

        for product in currentOrder.productsSold {
            if let countedProduct = productsCount[product.name] {
                productsCount[product.name] =  productsCount[product.name]! + 1
            } else {
                productsCount[product.name] = 1
            }
        }

Edit

Example modified to use Product instead of a String as the dictionary key.

First change Product to implement the Hashable and Equatable protocols:

struct Product: Hashable {
    var name: String
    var price: Double

    var hashValue: Int {
        get {
            return self.name.hashValue
        }
    }

    static func ==(lhs: Product, rhs: Product) -> Bool {
        return lhs.name == rhs.name
    }
}

Then change the type in the key dictionary to Product:

var productsCount = [Product: Int]()

for product in currentOrder.productsSold {
    if let countedProduct = productsCount[product] {
        productsCount[product] =  productsCount[product]! + 1
    } else {
        productsCount[product] = 1
    }
}
Jack G.
  • 3,681
  • 4
  • 20
  • 24
  • An NSCountedSet would be simpler. – Paulw11 Jun 13 '17 at 21:30
  • Thanks, it worked! Now, if I need each product with its count, how can I do that? Let's say I need: "product.name, product.color, product.price, … , productCount – shlagwuk Jun 14 '17 at 08:11