This is a variation of an older question (linked below), that I phrased poorly so doesn't adequately address my problem.
BACKGROUND
Suppose there is an apple basket with apples, each of which is an object with a size attribute, and in this particular basket, we have 3 apples: [{size:'S'}, {size:'M'}, {size:'L'}]
In the shopping process, each time a customer adds an apple to the cart, they are given the option to select a size, but importantly, they do not have to select a size, it is an optional selector.
I'm trying to write a method, remaining_options that maximizes the maximum number of options a customer is shown when they add an apple to their cart, given their history of past selections. The fact that the size selection is optional is very important. Consider these 2 examples:
Example A: Customer selects an option
- Customer adds 1st apple to cart
- Customer sees prompt
Please make a size selection (optional): [S, M, L] - Customer decides to select
S - Customer adds 2nd apple to cart
- Customer sees prompt
Please make a size selection (optional): [M, L]
Example B: Customer does NOT select an option
- Customer adds 1st apple to cart
- Customer sees prompt
Please make a size selection (optional): [S, M, L] - Customer skips this step
- Customer adds 2nd apple to cart
- Customer sees prompt
Please make a size selection (optional): [S, M, L]
In Example B, because the customer did NOT select an option, the full set of options available is still shown. The code does not arbitrarily "remove" an apple from the option-set. In other words, this method remaining_options is not responsible for calculating remaining quantity, only remaining options, which it tries to maximize.
NOTE: Of course, even though this method doesn't calculate quantity, upon real world checkout, inventory is adjusted for the next customer, again via a different method. I.e., assuming that in the examples above, customer does not make a size selection for the 2nd apple, then upon checkout...
- Example A, customer will receive 1
Sapple and a 2nd apple that's eitherMorL, as randomly selected by other code. If customer receives[S,M], then the next customer that orders an apple will only be able to add 1 apple andremaining_optionswill only return[L] - Example B, customer will receive 2 random apples, could be
[S,M],[M,L],[S,L], as randomly selected by other code. If customer receives[S,L], then the next customer that orders an apple will only be able to add 1 apple andremaining_optionswill only return[M]
COMPLEXITY
The challenge is I want remaining_options to work for any given number of possible attributes (size,color,price, etc.) on the apple object, and any number of options for such attributes. And since selections are optional, user can decline to select size,color,price,etc., or select 1 (e.g., only size) or 2 (e.g., size and price) and etc.
With this complexity, it's important that the code not treat the process of adding an apple to the cart as an independent process, where the remaining_options is calculated by options in basket minus the last option selected.
The code should instead look at all the apples in the customer's cart, whether or not an option has been selected for each, and then calculating the maximum number of remaining_options
The solutions in the previous posted question didn't work because they did the former, i.e., treated adding each apple as an independent process that removed an option from the basket.
Here's an example to clarify. Suppose the basket of 3 apples looks like this:
[
{size:'S', price:1},
{size:'M', price:2},
{size:'L', price:2},
]
Step 1 - adding the 1st apple
Let's say remaining_options takes an argument of the customer's existing. So when a customer adds their 1st apple to the cart, there's nothing in the cart existing, so all options are returned
basket.remaining_options([])
=> {size:['S','M','L'], price: [1,2]}
Step 2 - adding the 2nd apple
To finish up the 1st apple transaction, customer decided to select an apple with price:2. Then they add their 2nd apple to the cart. Since there's more than 1 apple with price:2, the customer's 1st choice created no capacity constraints, an again all options are shown.
basket.remaining_options([{price:2}])
=> {size:['S','M','L'], price: [1,2]}
NOTE: @obiruby astutely observed that although {size:['S','M','L'], price: [1,2]} is technically all the remaining options, they're not all selectable. For this second apple, if customer selects M, then the first apple has to be auto-assigned as L and therefore is no longer selectable. This is handled by yet another method that I already have working. So many methods... all to make this max-options optimization work!
Step 3 - adding the 3rd apple
To finish up the 2nd apple transaction, customer decided to, again, select an apple with price:2. Then they add their 3rd apple to the cart. Now it's tricky. If this code just subtracted options selected, then on face value, remaining_options might return this: {size:['S','M','L'], price: [1]}. But this is not accurate, because when all the price:2 apples are taken, so are all the M and L apples by default. This is why the right code needs to look collectively at existing cart. I.e., this should be the result:
basket.remaining_options([{price:2}, {price:2}])
=> {size:['S'], price: [1]}
Note also that because of how capacity constraints work, the following should also be returned correctly by the method:
basket.remaining_options([{price:2}, {size:'M'}])
=> {size:['S'], price: [1]}
# user's 1st ordered apple has a price request
# user's 2nd ordered apple has a size request
# COMBO of the 2 only work if...
# user's 1st ordered apple is the {price:2, size:'L'} apple in basket
# user's 2nd ordered apple is the {size:'M', price:'2'} apple in basket
# therefore if user is looking at remaining options for a 3rd apple, there's only the {size:'S', price:'1'} apple left in basket
So... yeah help greatly appreciated in writing this method! I have my non-working ideas in the old post. In looking at that proposed solution (which alas also doesn't look collectively at the existing cart), I realized that one solution could be to map out all the possible combinations of apples in basket that could be assigned to fulfill the requests of each apple in the cart, and picking the combination of assignments that yields the highest number of remaining options. But I feel like this solution would be wildly inefficient as baskets, carts, and possible options increased in size.