简体   繁体   中英

Ramda: Rejects from predicate array

https://jsbin.com/ziyufuxacu/edit?html,js,console,output

How can I report the rejects of an array of predicates and only the first reject

This was my attempt but I feel like there are ramda functions that can make this much more generic, for example if I had 10 predicates

var selected = [{
  Label: "a",
  Invalid: false,
  Deleted: true
}, {
  Label: "b",
  Invalid: false,
  Deleted: false
}, {
  Label: "c",
  Invalid: true,
  Deleted: false
}];


var canEditPredicates = [
  R.propEq("Deleted", false),
  R.propEq("Invalid", false),
  R.propEq("Label", "c")
];




var deleted = R.reject(canEditPredicates[0], selected);
var invalid = R.reject(canEditPredicates[1], R.without(deleted, selected));
var names = R.reject(canEditPredicates[2], R.without(deleted, R.without(invalid, selected)));

var rv = "The following items are bad:";



if (deleted.length) {
  rv += "\r\nDeleted items: " + R.join(", ")(R.pluck("Label")(deleted));
}

if (invalid.length) {
  rv += "\r\ninvalid items: " + R.join(", ")(R.pluck("Label")(invalid));
}
if (names.length) {
  rv += "\r\nnames items: " + R.join(", ")(R.pluck("Label")(names));
}


console.log(rv);

Output

The following items are bad:
Deleted items: a
invalid items: c
names items: b

To know why an item was rejected (the index of the predicate), you can use a flipped R.findIndex that takes an array of predicates. The predicates should be complemented, because we want true for a failed answer. The value passed to fins should be wrapped with R.applyTo, because R.find expects a predicate function.

Then you can map the array of items, pass the values, and get the predicate index of each failed item. If the item passed all checks (failed complemented), you'll get -1 instead of a predicate index.

 const { propEq, map, pipe, applyTo, flip, findIndex, complement } = R; const canEditPredicates = [ propEq("Deleted", false), propEq("Invalid", false), propEq("Label", "c") ]; const findFailedPredicateIndex = pipe(applyTo, flip(findIndex)(map(complement, canEditPredicates))); const findFailed = map(findFailedPredicateIndex); const selected = [{"Label":"a","Invalid":false,"Deleted":true},{"Label":"b","Invalid":false,"Deleted":false},{"Label":"c","Invalid":true,"Deleted":false},{"Label":"c","Invalid":false,"Deleted":false}]; const result = findFailed(selected); console.log(result);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>

This version takes several liberties with your input. They may not be justified. But they seem to me to make for a more logical system. Feel free to disregard if the changes go too far.

The biggest thing this does is to reverse the sense of the predicates. These versions are now listed as cannotEditPredicates rather than canEditPredicates . This allows us to write them more simply. For instance, the Deleted predicate can be just R.prop("Deleted") rather than R.prop('Deleted', false) .

I also bundle them differently to allow us to capture the failure results more cleanly. Instead of an array, they are collected in an object keyed by the name you used in your expected output.

This gives us an output structure like

{
  Deleted: ["a", "e"],
  invalid: ["c"],
  name: ["b"]
}

But we could trivially change it to include the whole object instead of the labels.

This is what it looks like:

 const rejectPreds = (selected, cannotEditPredicates) => R.reduce ( (output, item) => { const rec = R.find (([k, v]) => v (item))(R.toPairs (cannotEditPredicates)) return rec == undefined ? output : {...output, [rec [0]]: [...output [rec [0]], item .Label]} }, R.map (v => []) (cannotEditPredicates), selected ) const selected = [ {Label: "a", Invalid: false, Deleted: true}, {Label: "b", Invalid: false, Deleted: false}, {Label: "c", Invalid: true, Deleted: false}, {Label: "d", Invalid: false, Deleted: false}, {Label: "e", Invalid: false, Deleted: true} ]; const cannotEditPredicates = { Deleted: R.prop("Deleted"), invalid: R.prop("Invalid"), name: R.propEq("Label", "b") } const rejected = rejectPreds (selected, cannotEditPredicates) console .log (rejected) console .log (`The following items are bad: ${Object .entries (rejected) .map (([n, v]) => `${n}: [${[...v]}]`) .join ('\\n')} `)
 .as-console-wrapper {max-height: 100% !important; top: 0}
 <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>

Note that although this uses various Ramda functions, almost all of them could trivially be replaced with the versions on Array.prototype . The only place where that's more difficult is the map call, which Ramda runs on objects in the fairly obvious way. It would be easy enough to write a mapObject function in plain JS using a dance of Object.entries , Array.prototype.map and Object.fromEntries , if so desired.

I don't have time to play with this right now, but I think it might be worth investigating using an Either or Result implementation here instead. That might lead to more logical code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM