I think it would make sense to reprocess your data first:
Set
checkboxes don’t need to be a list of objects (e.g. [{id:1},{id:2}]). It can simply be a list of ids (e.g. [1,2])
- I’m not sure there’s much point in listing all accepted amounts. Just specify the maximum value each amount can have
So transform:
{id: 2, checkboxes: [{id: 30}, {id: 40}], valid_amount: [1,2]},
into:
{id: 2, checkboxes: [30, 40], max_amount: 2}
Here’s a function for it:
const transformSet = set => ({
id: set.id,
checkboxes: map(prop('id'), set.checkboxes),
max_amount: last(set.valid_amount)
});
Checkboxes
It seems (correct me if I’m wrong) that you only care about the objects for which value is set to true. In this case I’d would remove those for which value is set to false and keep only the ids.
So transform
[
{id: 10, value: true},
{id: 20, value: true},
{id: 30, value: true},
{id: 40, value: true},
{id: 50, value: true},
{id: 60, value: false},
{id: 70, value: true},
{id: 80, value: false},
]
into:
[10, 20, 30, 40, 50, 70]
Here’s a function for it:
const transformCheckboxes = into([], compose(
reject(propEq('value', false)),
map(prop('id'))
));
Why?
Let’s take set #1, finding whether an answer has been submitted can be as simple as finding the intersection of two arrays:
intersection([10, 20], [10, 20, 30, 40, 50, 70])
//=> [10, 20]
Then you need to make sure that the length of the array isn’t greater than the maximum amount for that set:
const selectCheckboxes = curry((checkboxes, set) =>
pipe(intersection, length, gte(set.max_amount))
(set.checkboxes, checkboxes));
Now you can iterate over your sets and you return the first set for which selectCheckboxes doesn’t return true. (Notice the use of complement(selectCheckboxes):
const findError = useWith(find, [
compose(complement(selectCheckboxes), transformCheckboxes),
map(transformSet)]);
const {curry, into, intersection, length, gte, map, prop, reject, last, useWith, find, compose, propEq, complement, pipe} = R;
const sets = [
{id: 1, checkboxes: [{id: 10}, {id: 20}], valid_amount: [1]},
{id: 2, checkboxes: [{id: 30}, {id: 40}], valid_amount: [1,2]},
{id: 3, checkboxes: [{id: 50}, {id: 60}, {id: 70}, {id: 80}], valid_amount: [1,2,3,4]},
];
const submittedCheckboxes = [
{id: 10, value: true},
{id: 20, value: true},
{id: 30, value: true},
{id: 40, value: true},
{id: 50, value: true},
{id: 60, value: false},
{id: 70, value: true},
{id: 80, value: false},
];
const transformSet = set => ({
id: set.id,
checkboxes: map(prop('id'), set.checkboxes),
max_amount: last(set.valid_amount)
});
const transformCheckboxes = into([], compose(
reject(propEq('value', false)),
map(prop('id'))
));
const selectCheckboxes = curry((checkboxes, set) =>
pipe(intersection, length, gte(set.max_amount))
(set.checkboxes, checkboxes));
const findError = useWith(find, [
compose(complement(selectCheckboxes), transformCheckboxes),
map(transformSet)]);
console.log(
findError(submittedCheckboxes, sets)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>