16
\$\begingroup\$

Introduction and credit

Assume you're a bartender. You have many happy people in your bar at most times, but many only ever drink the very same drink and too few for your taste and you want to change that. So you introduce a system where the price of a drink is variable, depending on how many have already been sold, but never more or less expensive than certain thresholds. For some odd reason you always forget to keep proper track of all sold drinks and prices and thus you need to think of a short (= memorable!) piece of code that does the math for you given the amount of drinks consumed.

This challenge has already appeared in the mid-term exam in 2012 at the functional programming course at my uni and I've got my professor's ok to post it here. We have been provided an example solution in the exam's language.

Input

Your input will be a list of strings which don't contain spaces - these are the names of the drinks sold. Take the input using your preferred, generally accepted input method.

Output

Your output will be a single number - this is the income you have generated this evening. Give the output using your preferred, generally accepted output method.

What to do?

This applies for each drink individually:

  • The starting price is 10.
  • Each time the drink is bought, it's price is bumped up by 1 for the next buyer.
  • The maximal price is 50. If the drink has been bought for 50 the new price will be 10 again.

Your task is to find the overall income, generated by the input list of drinks given the above rules.


In case you're wondering: "50 bucks is really damn expensive for a drink!", this is 50-deci Bucks, so 50 * 0.1 * Unit, but I've opted to go for 10-50 to not exclude languages without floating point arithmetic.

Who wins?

This is , so the shortest code in bytes wins! Standard rules apply.

Potential Corner Cases

If the input list is empty, the output shall be 0.
The input list icannot be assumed to be sorted by drink.

Examples

[] -> 0
["A"] -> 10
["A","B"] -> 20
["A","A","B"] -> 31
["A","B","A"] -> 31
["A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A"] -> 1240
["A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","B","B","B","C","C","D"] -> 1304 
["D","A","A","C","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","B","B","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","B","C"] -> 1304
["A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","B","B","B","C","C","D","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A"] -> 1304
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Props on asking your professor before posting, quite the O.G. move. \$\endgroup\$ Dec 10, 2016 at 0:14

13 Answers 13

4
\$\begingroup\$

JavaScript (ES6), 50 bytes

a=>a.map(x=>t+=d[x]=d[x]<50?d[x]+1:10,t=0,d={})&&t
\$\endgroup\$
3
  • \$\begingroup\$ Where do you init d[x] to 10? \$\endgroup\$
    – Titus
    Dec 22, 2016 at 23:04
  • \$\begingroup\$ @Titus If d[x] has not been set, it's undefined; this makes d[x]<50 return false, so d[x]=d[x]<50?d[x]+1:10 sets d[x] to 10. \$\endgroup\$ Dec 22, 2016 at 23:57
  • \$\begingroup\$ I keeü forgetting that JS has undefined. :) \$\endgroup\$
    – Titus
    Dec 23, 2016 at 0:06
4
\$\begingroup\$

Python 2, 79 74 54 48 Bytes

Massive byte count increase by rethinking the problem. I would like to get rid of the int cast but my brain isn't working. Making use of l.pop() to avoid trimming the list twice and some good old lambda recursion :)

f=lambda l:l and l.count(l.pop())%41+10+f(l)or 0

thanks to Jonathan Allan for saving 6 bytes :)

My old 54-byte version I was quite proud of :)

f=lambda l:int(l>[])and~-l.count(l[0])%41+10+f(l[1:])
\$\endgroup\$
4
  • \$\begingroup\$ ...l>[]and 1*~... to save those 3 bytes you knew you could. \$\endgroup\$ Dec 10, 2016 at 3:29
  • \$\begingroup\$ In fact 1 less with: f=lambda l:l and~-l.count(l[0])%41+10+f(l[1:])or 0 \$\endgroup\$ Dec 10, 2016 at 3:31
  • \$\begingroup\$ Oooh and another two with: f=lambda l:l and l.count(l.pop())%41+10+f(l)or 0 \$\endgroup\$ Dec 10, 2016 at 3:47
  • \$\begingroup\$ @JonathanAllan thanks for the tips! I'll update my post soon :) \$\endgroup\$
    – Kade
    Dec 10, 2016 at 18:12
2
\$\begingroup\$

Pyth, 15 bytes

ssm<*lQ}T50/Qd{

A program that takes input of a list and prints the result.

Test suite (First line to allow multiple input)

How it works

ssm<*lQ}T50/Qd{   Program. Input: Q
ssm<*lQ}T50/Qd{Q  Implicit input fill
              {Q  Deduplicate Q
  m               Map over that with variable d:
       }T50        Yield [10, 11, 12, ..., 48, 49, 50]
    *lQ            Repeat len(Q) times
   <       /Qd     First Q.count(d) elements of that
 s                Flatten
s                 Sum
                  Implicitly print
\$\endgroup\$
2
\$\begingroup\$

Jelly, 14 11 10 bytes

50⁵rṁЀĠSS

TryItOnline!

How?

50⁵rṁЀĠSS - Main link: list of drink names                e.g. ['d', 'a', 'b', 'a', 'c']
       Ġ   - group indices by values                       e.g. [[2, 4], [3], [5], [1]]
  ⁵        - 10
50         - 50
   r       - inclusive range, i.e. [10, 11, 12, ..., 48, 49, 50]
    ṁЀ    - mould left (the range) like €ach of right(Ð)  e.g. [[10, 11], [10], [10], [10]]
                 note: moulding wraps, so 42 items becomes [10, 11, 12, ..., 48, 49, 50, 10]
        S  - sum (vectorises)                              e.g. [40, 11]
         S - sum                                           e.g. 51
\$\endgroup\$
2
\$\begingroup\$

05AB1E, 16 15 bytes

Thanks to Emigna for saving a byte!

ÎÙv¹y¢L<41%T+OO

Uses the CP-1252 encoding. Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ ÎÙv¹y¢L<41%T+OO should work for 1 byte saved \$\endgroup\$
    – Emigna
    Dec 10, 2016 at 12:05
  • \$\begingroup\$ @Emigna That's nice! Thanks :) \$\endgroup\$
    – Adnan
    Dec 11, 2016 at 10:34
1
\$\begingroup\$

Perl 41 Bytes

Includes +1 for -p

$\+=$H{$_}+=$H{$_}?$H{$_}>49?-40:1:10;}{

Takes input on newlines.

Increments a hash value by: 10 if it's undef, -40 if it's > 49 i.e. 50, or 1 otherwise. This is then added to $\, the output separator, which the -p prints.

Example:

$ echo -e 'A\nB\nA' | perl -pe '$\+=$H{$_}+=$H{$_}?$H{$_}>49?-40:1:10;}{'
31
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 13 bytes

{.¡€gL<41%T+O

Explanation

["A","B","A"] used as example.

{               # sort input
                # STACK: ["A","A","B"]
 .¡             # split on different
                # STACK: [["A","A"],["B"]]
   €g           # length of each sublist
                # STACK: [2,1]
     L          # range [1 ... x] (vectorized)
                # STACK: [1,2,1]
      <         # decrease by 1
                # STACK: [0,1,0]
       41%      # mod 41
                # STACK: [0,1,0]
          T+    # add 10
                # STACK: [10,11,10]
            O   # sum
                # OUTPUT: 31
\$\endgroup\$
0
1
\$\begingroup\$

C++14, 105 bytes

As generic unnamed lambda returning via reference parameter. Requires input to be a container of string that has push_back, like vector<string>.

Using the %41+10 trick from Kade's Python answer.

[](auto X,int&r){r=0;decltype(X)P;for(auto x:X){int d=0;for(auto p:P)d+=x==p;r+=d%41+10;P.push_back(x);}}

Creates an empty container P as memory what has been served already. The price is calculated by counting the x in P.

Ungolfed and usage:

#include<iostream>
#include<vector>
#include<string>

using namespace std;

auto f=
[](auto X, int& r){
  r = 0;
  decltype(X) P;
  for (auto x:X){
    int d = 0;
    for (auto p:P)
      d += x==p;
    r += d % 41 + 10;
    P.push_back(x);
  }
}
;

int main(){
 int r;
 vector<string> V;
 f(V,r);cout << r << endl;
 V={"A"};
 f(V,r);cout << r << endl;
 V={"A","B"};
 f(V,r);cout << r << endl;
 V={"A","B","C"};
 f(V,r);cout << r << endl;
 V={"A","A"};
 f(V,r);cout << r << endl;
 V={"A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A"};
 f(V,r);cout << r << endl;
 V={"A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","B","B","B","C","C","D"};
 f(V,r);cout << r << endl;
 V={"A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","A","B","C"};
 f(V,r);cout << r << endl;
}
\$\endgroup\$
0
\$\begingroup\$

Mathematica, 64 bytes

Feels like it should be shorter.

Tr[(19+#)#/2&/@(Length/@Gather@#//.z_/;z>41:>Sequence[41,z-41])]&

Length/@Gather@# counts the repetitions of each drink. //.z_/;z>41:>Sequence[41,z-41] splits any integer z exceeding 41 in this into 41 and z-41, to reflect the price drop. Then each of the counts is plugged into the formula (19+#)#/2, which is the total cost of # drinks as long as # is at most 41. Finally, Tr sums up those costs.

\$\endgroup\$
0
\$\begingroup\$

k, 22 bytes

The argument can be any list - strings, numerics etc.

{+/,/10+(#:'=x)#\:!41}

The q translation is easier to read:

{sum raze 10+(count each group x)#\:til 41}
\$\endgroup\$
0
\$\begingroup\$

C#, 193 bytes + 33

An extra 33 bytes for using System.Collections.Generic;

void m(string[]a){int t=0;Dictionary<string,int>v=new Dictionary<string,int>();foreach(string s in a){if(v.ContainsKey(s)){v[s]=v[s]==50?10:v[s]+1;}else{v.Add(s,10);}t+=v[s];}Console.WriteLine(t);}

I'm sure this can be golfed to oblivion, Dictionaries are definitely not the best way to do this and I could probably work a ternary into my if. Aside from that I think it's okay!

Examples:

a = {"A", "A", "A", "B", "B", "C"};
//output = 64

a = {"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A",, "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A", "A" ,"A" "A" ,"A", "A" ,"A", "B", "B", "C"};
//output 727

Ungolfed

void m(string[] a)
{
    int t=0;
    Dictionary<string,int> v = new Dictionary<string,int>();
    foreach(string s in a)
    {
        if(v.ContainsKey(s))
        {
            v[s]=v[s]==50?10:v[s]+1;
        }
        else
        {
            v.Add(s,10);
        }
        t+=v[s];
    }
    Console.Write(t);
}
\$\endgroup\$
0
\$\begingroup\$

Clojure, 79 bytes

#(apply +(mapcat(fn[l](for[i(range l)](+(mod i 41)10)))(vals(frequencies %)))))

Counts frequencies of drinks, then calculates the base price as 10 + (i % 41). mapcat concatenates them and apply + calculates the sum.

\$\endgroup\$
0
\$\begingroup\$

PHP, 47 bytes

while($k=$argv[++$i])$s+=10+$p[$k]++%41;echo$s;

takes input from command line arguments; run with -r.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.