loading...

Daily Challenge #74 - Free Pizza

thepracticaldev profile image dev.to staff ・1 min read

In an attempt to boost sales, the manager of the pizzeria you work at has devised a new pizza rewards system. Although, he isn't exactly sure what he wants the variables to be. The rewards system may change in the future. Your manager wants you to implement a function that, given a dictionary of customers, a minimum number of orders, and a minimum order value, returns a set of the customers who are eligible for a reward.

Customers in the dictionary are represented as:
{ 'customerName' : [list_of_order_values_as_integers] }

Test 1:
Make at least 5 orders of at least 20$ each and get a free pizza!

min_orders = 5
min_price = 20
customers = {
'John Doe' : [22, 30, 11, 17, 15, 52, 27, 12], 
'Jane Doe' : [5, 17, 30, 33, 40, 22, 26, 10, 11, 45]
}

Test 2:
Make at least 2 orders of at least 50$ each and get a free pizza!

min_orders = 2
min_price = 50
customers = {
'Joey Bonzo' : [22, 67, 53, 29],
'Jennifer Bonzo' : [51, 19]
}

Test 3:
Make at least 3 orders of at least 15$ each and get a free pizza!

min_orders = 3
min_price = 15
customers = {
'Natsumi Sakamoto' : [15, 15, 14],
'Gorou Hironaka' : [15, 15, 15],
'Shinju Tanabe' : [120, 240]
}

This challenge comes from kingcobra on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

pic
Editor guide
 

Elm

module FreePizza exposing (Customer, freePizza)


type alias Customer =
    { name : String
    , purchases : List Int
    }


isEligible : Int -> Int -> Customer -> Bool
isEligible minimumOrders minimumPrice =
    .purchases
        >> List.filter ((<=) minimumPrice)
        >> List.length
        >> (<=) minimumOrders


freePizza : Int -> Int -> List Customer -> List Customer
freePizza minimumOrders minimumPrice =
    List.filter <| isEligible minimumOrders minimumPrice

Explainations

module FreePizza exposing (Customer, freePizza)

This will define anything that we choose to export to the outside world importing our module. In this case, we only want to expose our Customer type alias & freePizza function for our tests (see below).

type alias Customer =
    { name : String
    , purchases : List Int
    }

This will define a customer in our application. Note that I choosed to use a name field for the name of the customer and a purchases field for the total record of the customer's purchases.

isEligible : Int -> Int -> Customer -> Bool
isEligible minimumOrders minimumPrice =
    .purchases
        >> List.filter ((<=) minimumPrice)
        >> List.length
        >> (<=) minimumOrders

This will define the inner logic of the challenge. Basically what it does is:

  1. Grab the purchases out of the current iterated object (customer). Almost anything in Elm is a function, so is .purchases which is a function that accepts a customer and returns a list of integers (the purchases). .purchases : Customer -> List Int. It is equivalent to this in JavaScript: const { purchases } = customer;.
  2. Filter out all the customer's purchases that are not above or equal to the minimum order's price defined by our free pizza's policy. The ((<=) minimumPrice) part is equivalent to (\purchase -> purchase >= minimumPrice)
  3. Calculate the length of the filtered list.
  4. Check whether the length of the list is at least equal or above the minimum orders, which means that it will return true when the customer has made enough purchases.
freePizza : Int -> Int -> List Customer -> List Customer
freePizza minimumOrders minimumPrice =
    List.filter <| isEligible minimumOrders minimumPrice

This will filter out all the customer that are non-eligible to our current free pizza's policy.

Tests

module FreePizzaTest exposing (suite)

import Expect exposing (equal)
import FreePizza exposing (Customer, freePizza)
import Test exposing (Test, describe, test)


suite : Test
suite =
    describe "Free Pizza"
        [ test "it should return a set of customers who are eligible for a reward #1" <|
            \_ ->
                let
                    minimumOrders : Int
                    minimumOrders =
                        5

                    minimumPrice : Int
                    minimumPrice =
                        20

                    customers : List Customer
                    customers =
                        [ { name = "John Doe"
                          , purchases = [ 22, 30, 11, 17, 15, 52, 27, 12 ]
                          }
                        , { name = "Jane Doe"
                          , purchases = [ 5, 17, 30, 33, 40, 22, 26, 10, 11, 45 ]
                          }
                        ]

                    expectations : List Customer
                    expectations =
                        [ { name = "Jane Doe"
                          , purchases = [ 5, 17, 30, 33, 40, 22, 26, 10, 11, 45 ]
                          }
                        ]
                in
                equal expectations <| freePizza minimumOrders minimumPrice customers
        , test "it should return a set of customers who are eligible for a reward #2" <|
            \_ ->
                let
                    minimumOrders : Int
                    minimumOrders =
                        2

                    minimumPrice : Int
                    minimumPrice =
                        50

                    customers : List Customer
                    customers =
                        [ { name = "Joey Bonzo"
                          , purchases = [ 22, 67, 53, 29 ]
                          }
                        , { name = "Jennifer Bonzo"
                          , purchases = [ 51, 19 ]
                          }
                        ]

                    expectations : List Customer
                    expectations =
                        [ { name = "Joey Bonzo"
                          , purchases = [ 22, 67, 53, 29 ]
                          }
                        ]
                in
                equal expectations <| freePizza minimumOrders minimumPrice customers
        , test "it should return a set of customers who are eligible for a reward #3" <|
            \_ ->
                let
                    minimumOrders : Int
                    minimumOrders =
                        3

                    minimumPrice : Int
                    minimumPrice =
                        15

                    customers : List Customer
                    customers =
                        [ { name = "Natsumi Sakamoto"
                          , purchases = [ 15, 15, 14 ]
                          }
                        , { name = "Gorou Hironaka"
                          , purchases = [ 15, 15, 15 ]
                          }
                        , { name = "Shinju Tanabe"
                          , purchases = [ 120, 240 ]
                          }
                        ]

                    expectations : List Customer
                    expectations =
                        [ { name = "Gorou Hironaka"
                          , purchases = [ 15, 15, 15 ]
                          }
                        ]
                in
                equal expectations <| freePizza minimumOrders minimumPrice customers
        ]

Playground

Test it online here.

 

Javascript

function promo(order, price, customers) {
    return Object.keys(customers).filter(c => customers[c].filter(p => p >= price).length >= order);
}

// Test 1
promo(min_orders, min_price, customers)
// ["Jane Doe"]

// Test 2
promo(min_orders, min_price, customers)
// ["Joey Bonzo"]

// Test 3
promo(min_orders, min_price, customers)
// ["Gorou Hironaka"]
 

Elixir:

defmodule Day74 do
  def eligible(customers, min_orders, min_price) do
    for customer = {_, orders} <- customers,
        Enum.count(orders, &(&1 >= min_price)) >= min_orders,
        into: %{},
        do: customer
  end
end

Tests:

defmodule Day74Test do
  use ExUnit.Case

  test "5 of $20" do
    customers = %{
      "John Doe" => [22, 30, 11, 17, 15, 52, 27, 12],
      "Jane Doe" => [5, 17, 30, 33, 40, 22, 26, 10, 11, 45]
    }

    assert %{"Jane Doe" => _} = Day74.eligible(customers, 5, 20)
  end

  test "2 of $50" do
    customers = %{
      "Joey Bonzo" => [22, 67, 53, 29],
      "Jennifer Bonzo" => [51, 19]
    }

    assert %{"Joey Bonzo" => _} = Day74.eligible(customers, 2, 50)
  end

  test "3 of $15" do
    customers = %{
      "Natsumi Sakamoto" => [15, 15, 14],
      "Gorou Hironaka" => [15, 15, 15],
      "Shinju Tanabe" => [120, 240]
    }

    assert %{"Gorou Hironaka" => _} = Day74.eligible(customers, 3, 15)
  end
end
 

Here's one in TypeScript

My preferred way of passing bags of arguments to functions in JS is by destructuring an object, which gets around the whole lets-just-remember-the-second-argument-was-minOrders thing.

The hacky embedded test harness is just there to make this portable.


hmm, I think I found a bug in the codepen plugin, where the template string at the bottom is being styled like it never got closed.
Will find the repo and pop in a bug report.

 

My solution in js

const rewardsSystem = (minOrders = 0, minPrice = 0, customers = {}) => Object.keys(customers).filter((customer) => customers[customer].filter((value) => value >= minPrice).length >= minOrders);
 

c++, hope it's correct.

#include <iostream>
#include <unordered_set>
#include <utility>
#include <vector>

struct Customer{
    Customer(std::string t_name, std::vector<int> t_orders) : name(std::move(t_name)), orders(std::move(t_orders)){
    };

    std::string name;
    std::vector<int> orders;
};

auto freePizza(const std::vector<Customer>& customers, const int& minPrice, const int& minOrders){
    std::vector<Customer> returnValue{};

    for(const auto& customer : customers){
        if(customer.orders.size() < minOrders)
            continue;

        int count = 0;

        for(const auto& item : customer.orders){
            if(item >= minPrice)
                ++count;
        }

        if(count >= minOrders)
            returnValue.push_back(customer);
    }
    return returnValue;
}
 

Python solution

def rewarder(customers, orders, price):
    eligible = set()
    for key, value in customers.items():
        number_of_occurences = sum(num > price for num in value)
        if number_of_occurences >= orders:
            eligible.add(key)

    return eligible


x = rewarder(customers, min_orders, min_price)
print(x)
 

APL - Solution 1 (using Dyalog APL)

I'm passing the argument in a suitable array-format for convenience and brevity:

promo←{(⊂'')~⍨(⊂⍵){(⍺[1]≤+/⍺[2]≤2⊃⍵)/1⊃⍵}¨3⊃⍵}

Output:

      promo 5 20(('John Doe'(22 30 11 17 15 15 52 27 12))('Jane Doe'(5 17 30 33 40 22 26 10 11 45)))
┌────────┐
│Jane Doe│
└────────┘
      promo 2 50 (('Joey Bonzo'(22 67 53 29))('Jennifer Bonzo'(51 19)))
┌──────────┐
│Joey Bonzo│
└──────────┘
      promo 3 15 (('Natsumi Sakamoto'(15 15 14))('Gorou Hironaka'(15 15 15))('Shinju Tanabe'(120 40)))
┌──────────────┐
│Gorou Hironaka│
└──────────────┘

APL - Solution 2

Since the task was shown with JSON-Data, I want to also show how to tackle this with JSON-Data in APL.

promo2←{(⊂'')~⍨(⊂⍵.(min_orders min_price)){(⍺[1]≤+/⍺[2]≤⍵.hist)/⍵.id}¨⍵.customers}

Testing

      Test1←⎕JSON'{"min_orders":5,"min_price":20,"customers":[{"id":"John Doe","hist":[22, 30, 11 ,17 ,15 ,52, 27 ,12]},{"id":"Jane Doe","hist":[5 ,17 ,30 ,33, 40, 22, 26, 10, 11 ,45]}]}'
      promo2 Test1
┌────────┐
│Jane Doe│
└────────┘

      Test2←⎕JSON'{"min_orders":2,"min_price":50,"customers":[{"id":"Joey Bonzo","hist":[22,67,53,29]},{"id":"Jennifer Bonzo","hist": [51,19]}]}'
      promo2 Test2
┌──────────┐
│Joey Bonzo│
└──────────┘

      Test3←⎕JSON'{"min_orders":3,"min_price":15,"customers":[{"id":"Natsumi Sakamoto","hist":[15,15,14]},{"id":"Gorou Hironaka","hist": [15,15,15]},{"id":"Shinju Tanabe","hist":[120,40]}]}'
      promo2 Test3
┌──────────────┐
│Gorou Hironaka│
└──────────────┘

Feel free to experiment and Try it online! ;)

 

Swift solution:

import Foundation

/*
 Free Pizza Challenge Function

 @param customerDict a list of customer names(keys, String) with associated transaction lists(values, [Int])
 @param minNum is the minimum number of transactions over the specified dollar amount to be eligible for rewards
 @param minAmt is the dollar amount the transactions need to surpass to be eligible for rewards.

 @return a list of the customer dictionary entries which are all eligible for rewards.
 */
func customersWithRewards(customerDict:[String : [Int]],
                          minNum:Int,
                          minAmt:Int) -> [String : [Int]] {

    var rewarded:[String : [Int]] = [String : [Int]]()

    for customer in customerDict {  
        // filter all transactions, leaving only ones greater than or equal to the Min Amount
        let eligibleTransactions = customer.value.filter({ $0 >= minAmt})

        // see if there are enough transactions over the given cost, and if so add the customer and their transaction list to the dictionary to return.
        if (eligibleTransactions.count >= minNum) {
            rewarded[customer.key] = customer.value
        }
    }
    return rewarded
}




// variable set 1
let min_orders1 = 5
let min_price1 = 20
let customers1 = ["John Doe" : [22, 30, 11, 17, 15, 52, 27, 12],
                  "Jane Doe" : [5, 17, 30, 33, 40, 22, 26, 10, 11, 45]]

// variable set 2
let min_orders2 = 2
let min_price2 = 50
let customers2 = ["Joey Bonzo" : [22, 67, 53, 29],
                  "Jennifer Bonzo" : [51, 19]]

// variable set 3
let min_orders3 = 3
let min_price3 = 15
let customers3 = ["Natsumi Sakamoto" : [15, 15, 14],
                  "Gorou Hironaka" : [15, 15, 15],
                  "Shinju Tanabe" : [120, 240]]


// case 1
print("Eligible Customers for case 1:")
print(customersWithRewards(customerDict: customers1, minNum: min_orders1, minAmt: min_price1))
print("\n\n")

// case 2
print("Eligible Customers for case 2:")
print(customersWithRewards(customerDict: customers2, minNum: min_orders2, minAmt: min_price2))
print("\n\n")

// case 3
print("Eligible Customers for case 3:")
print(customersWithRewards(customerDict: customers3, minNum: min_orders3, minAmt: min_price3))
print("\n\n")

output:

Eligible Customers for case 1:
["Jane Doe": [5, 17, 30, 33, 40, 22, 26, 10, 11, 45]]


Eligible Customers for case 2:
["Joey Bonzo": [22, 67, 53, 29]]


Eligible Customers for case 3:
["Gorou Hironaka": [15, 15, 15]]


Program ended with exit code: 0

I choose to loop through the customer list and filter out transactions in a separate statements just for a nice easy read.
Although I'm pretty sure that could be shortened down with a bit by calling .filter() on the customer list itself and nesting another .filter() for the transactions inside that closure... but with all the mess of nesting closures that would be needed, I just felt like readability rather than shortness of code wins the day on this one.