I've been using Folktale's Validation on a new project and I've found it really useful, but I have hit a wall with the need for sequential validations. I have a config object and I need to perform the following validations:
is is an Object?
are the object's keys valid (do they appear on a whitelist)?
are the values of the keys valid?
Each validation depends on the previous validation - if the item isn't an object, validating its keys is pointless (and will error), if the object has no keys, validating their values are pointless. Effectively I want to short-circuit validation if the validation fails.
My initial thought was to use Result instead of Validatio, but mixing the two types feels confusing, and I already havevalidateIsObject` defined and used elsewhere.
My current (working but ugly) solution is here:
import { validation } from 'folktale';
import { validateIsObject } from 'folktale-validations';
import validateConfigKeys from './validateConfigKeys';
import validateConfigValues from './validateConfigValues';
const { Success, Failure } = validation;
export default config => {
const wasObject = validateIsObject(config);
let errorMessages;
if (Success.hasInstance(wasObject)) {
const hadValidKeys = validateConfigKeys(config);
if (Success.hasInstance(hadValidKeys)) {
const hasValidValues = validateConfigValues(config);
if (Success.hasInstance(hasValidValues)) {
return Success(config);
}
errorMessages = hasValidValues.value;
} else {
errorMessages = hadValidKeys.value;
}
} else {
errorMessages = wasObject.value;
}
return Failure(errorMessages);
};
I initially took the approach of using nested matchWiths, but this was even harder to read.
How can I improve on this solution?
You can write a helper that applies validation rules until a Failure is returned. A quick example:
const validateUntilFailure = (rules) => (x) => rules.reduce(
(result, rule) => Success.hasInstance(result)
? result.concat(rule(x))
: result,
Success()
);
We use concat to combine two results. We use Success.hasInstance to check whether we need to apply the next rule. Your module will now be one line long:
export default config => validateUntilFailure([
validateIsObject, validateConfigKeys, validateConfigValues
]);
Note that this implementation doesn't return early once it sees a Failure. A recursive implementation might be the more functional approach, but won't appeal to everyone:
const validateUntilFailure = ([rule, ...rules], x, result = Success()) =>
Failure.hasInstance(result) || !rule
? result
: validateUntilFailure(rules, x, result.concat(rule(x)))
Check out the example below for running code. There's a section commented out that shows how to run all rules, even if there are Failures.
const { Success, Failure } = folktale.validation;
const validateIsObject = (x) =>
x !== null && x.constructor === Object
? Success(x)
: Failure(['Input is not an object']);
const validateHasRightKeys = (x) =>
["a", "b"].every(k => k in x)
? Success(x)
: Failure(['Item does not have a & b.']);
const validateHasRightValues = (x) =>
x.a < x.b
? Success(x)
: Failure(['b is larger or equal to a']);
// This doesn't work because it calls all validations on
// every item
/*
const validateItem = (x) =>
Success().concat(validateIsObject(x))
.concat(validateHasRightKeys(x))
.concat(validateHasRightValues(x))
.map(_ => x);
*/
// General validate until failure function:
const validateUntilFailure = (rules) => (x) => rules.reduce(
(result, rule) => Success.hasInstance(result)
? result.concat(rule(x))
: result,
Success()
);
// Let's try it out!
const testCases = [
null,
{ a: 1 },
{ b: 2 },
{ a: 1, b: 2 },
{ a: 2, b: 1 }
];
const fullValidation = validateUntilFailure([
validateIsObject,
validateHasRightKeys,
validateHasRightValues
]);
console.log(
testCases
.map(x => [x, fullValidation(x)])
.map(stringifyResult)
.join("\n")
);
function stringifyResult([input, output]) {
return `input: ${JSON.stringify(input)}, ${Success.hasInstance(output) ? "success:" : "error:"} ${JSON.stringify(output.value)}`;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/folktale/2.0.1/folktale.min.js"></script>
Related
I have a dynamic layout within a React Native view - that changes based on provided data for the particular data set. Ie there could be a provided Guide, Inventory, or Observation list that need displaying - but there may just be a combination of one, two or 3 of these things provided within any dataset.
I need to supply conditional styles / display object throughout the view based on the state of each provided data type.
Currently I have a series of checks that return true if the state is populated i.e:
const hasSiteGuide = () => {
if (!this.isEmpty(this.state.locationSiteGuidePdf)) {
return true;
}
};
I then have a series of functions that determine how many items are supplied and provide the relevant style - here are the logic checks:
const layout_full = () => {
if (hasSiteGuide() && hasInventoryItems() && hasObservations()) {
return true;
}
};
const layout_one = () => {
if (!hasSiteGuide() && !hasInventoryItems() && !hasObservations()) {
return true;
}
};
const layout_two = () => {
if (!hasSiteGuide() || !hasInventoryItems() !hasObservations()) {
if (hasSiteGuide() || hasInventoryItems()) {
return true;
}
}
};
the checks are used to display conditional styles in multiple areas - for example:
<View
style={[
layout_full() && RP_Styles.btnWrap,
layout_three() && RP_Styles.btnWrap_three,
layout_two() && RP_Styles.btnWrap_two,
layout_one() && RP_Styles.btnWrap_one,
]}>
My question is - i'm sure that there is a better way to do this.. particularly as i'm about to implement a 4th possibility - I was wondering if there is a way that I could use just one function to count how many true statements are in a list and return the relevant condition
ie:
returnTrueCount(hasSiteGuide(), hasInventoryItems(), hasObservations()). - is this possible? Or has anybody got an idea for a better approach please? Thanks very much
If you want do something like this : returnTrueCount(hasSiteGuide(), hasInventoryItems(), hasObservations()) then I think the following solution would would work.
const a = () => true;
const b = () => true;
const c = () => false;
const d = () => true;
function returnTrueCount(...funcs) {
return funcs.reduce((a, f) => a + +f(), 0);
}
console.log(returnTrueCount(a, b, c, d));
This worked for me:
const returnTrueCount = (array, value) => {
return array.filter((v) => v === value).length;
};
called with a basic array and the value to check.
Imagine I have a data model like so:
// Pseudo-code, as I don't know how to achieve this
const Model = {
name: String.required,
description: String.optional,
rank: Number.required,
// ...
}
Basically, that means the Object must have a property name and it has to be a String. The property description is optional, but has to be a String if defined. Etc.
Now, let's say I have a bunch of Objects :
const obj1 = {
name: 'Object 1',
rank: 1,
}
const obj2 = {
description: 'This is object 2',
rank: 2,
}
In order to save my objects in a NoSQL database (Firebase's Firestore), I'd like to be sure that each Object matches the Model. This would prevent having missing fields for instance.
In my example, obj1 should pass but obj2 shouldn't has the propery name is missing.
And is there a way to throw in the error message which properties are missing?
Solution:
You can create a simple function that will validate your models based on an Array of required and optional parameters.
Note: The below has required and optional parameters hardcoded within the function, but you can also pass them in with very little effort and change in code. Simply remove the declarations and add them as parameters to the function.
Code
function validateModel(obj) {
let
// parameters specification
required = ["name", "rank"],
optional = ["description"],
// helper methods
toObjects = (a, v) => Reflect.has(obj, v) ? (a[v] = obj[v], a) : a,
grab = (type) => type.reduce(toObjects, {}),
payload = () => Object.assign(grab(required), grab(optional)),
notValid = v => !Reflect.has(obj, v);
//output
if (required.some(notValid)) return false;
return payload();
}
Working Example:
//setup
let log = (x) => { return console.log(x), log };
const Model={name:"n",description:"d",rank:"r"},Model2={rank:"a"},Model3={rank:"b",name:"b"};
// Validation Method:
function validateModel(obj) {
let
// parameters specification
required = ["name", "rank"],
optional = ["description"],
// helper methods
toObjects = (a, v) => Reflect.has(obj, v) ? (a[v] = obj[v], a) : a,
grab = (type) => type.reduce(toObjects, {}),
payload = () => Object.assign(grab(required), grab(optional)),
notValid = v => !Reflect.has(obj, v);
//output
if (required.some(notValid)) return false;
return payload();
}
// Examples:
log
(validateModel(Model) || "Invalid Model")
(validateModel(Model2) || "Invalid Model")
(validateModel(Model3) || "Invalid Model")
Making it Multi-Purpose:
If you create something like a model checker, it would be best to be able to check multiple types of Models with it. The below code creates a constructor that takes an Object full of Structs. Each Struct contains an array of required and optional parameters.
The ModelChecker constructor returns an object with a single method, validate. By passing in the object you want to check and the name of the model structure you want to check against, you can determine if a Model is valid.
Working Example:
//setup
let log = (x) => { return console.log(x), log };
const Model={name:"n",description:"d",rank:"r"},Model2={rank:"a"},Model3={rank:"b",name:"b"}, Car={"color":"black", "name": "toyota" }, Car2={"name":"chevy"};
//setup validation structures
const structs = {
"identity": {
required: ["name", "rank"],
optional: ["description"]
},
"car": {
required: ["color"],
optional: ["name"]
}
}
// model checker constructor
function ModelChecker(structs) {
let proto = {
structs
};
proto.validate = function validateModel(obj, struct) {
if (!this.structs[struct]) return false;
let
// parameters specification
required = this.structs[struct].required,
optional = this.structs[struct].optional,
// helper methods
toObjects = (a, v) => Reflect.has(obj, v) ? (a[v] = obj[v], a) : a,
grab = (type) => type.reduce(toObjects, {}),
payload = () => Object.assign(grab(required), grab(optional)),
notValid = v => !Reflect.has(obj, v);
//output
if (required.some(notValid)) return false;
return payload();
}
return proto;
}
// Examples:
let mc = ModelChecker(structs);
log
(mc.validate(Model, "identity") || "Invalid Model")
(mc.validate(Model2, "identity") || "Invalid Model")
(mc.validate(Model3, "identity") || "Invalid Model")
(mc.validate(Car, "car") || "Invalid Model")
(mc.validate(Car2, "car") || "Invalid Model")
I'm starting or trying to learn functional programming monads.
So the first is Maybe. I'm trying to convert the code with maybe monad.
function(fieldName, vals, fields) {
var newValue = vals[fieldName];
if (typeof(newValue) == 'undefined') {
var elemFrom = fields[fieldName];
if (elemFrom) {
newValue = fields[fieldName]
}
}
if (typeof (newValue) != 'undefined') {
return newValue
}
}
Here I have a bunch of checks for undefined which i think is good use of monay.
My problem is that I read that you pass value to the maybe monad and map function.
However in my case I replace the value inside the monad.
If I pass null the map method will do nothig since the value is undefined.
I'm not using a framework, i want simple implementation so I can understand it.
Should I add "else" method in the maybe monad class (function).
I have the opposite case "Do something if the value is undefined"
Can you suggest how to solve the issue
Thank you
So the function you posted could be rewritten as
const f = (a, b, c) => b[a] === undefined ? c[a] : b[a];
It isn't clear to me that this needs to be a function at all rather than being inlined wherever you want to use the relevant object properties, but maybe you're partially applying it or something, I'm not judging.
As for Maybe, a (very simple) implementation might look something like this:
class Maybe {
static of (value) {
return new Maybe(value);
}
// Proper solution here should be recursive to handle
// nesting properly, but I'm lazy
static equals (a, b) {
return a.chain(x => x) === b.chain(x => x);
}
constructor(value) {
this._value = value;
}
map (f) {
// Does not distinguish null from undefined, but YMMV. Note
// that if the Maybe value is null or undefined we never touch
// f, that's the null propagation thing.
return this._value == null ? this : new Maybe(f(this._value));
}
chain (f) {
const result = this._value == null ? this : f(this._value);
console.assert(result instanceof Maybe);
return result;
}
}
Now we can test that it obeys the Monad laws:
const a = 3;
const f = x => Maybe.of(x * x);
Maybe.of(a).chain(f) === f(a) // left identity
Maybe.equals(Maybe.of(5).chain(Maybe.of), Maybe.of(5)); // right identity
And that it's a valid Functor
Maybe.equals(Maybe.of(3).map(x => x), Maybe.of(3)); // identity
Maybe.equals( // composition
Maybe.of(3).map(x => x + 2).map(x => x * 3),
Maybe.of(3).map(compose(x => x * 3, x => x + 2))
);
Sweet.
So now, to your function. It would be rewritten as
const f = (a, b, c) => {
return b[a] === undefined ? Maybe.of(c[a]) : Maybe.of(b[a]);
}
Perhaps you see now the reason for my confusion, Maybe isn't really saving you much here. But if I were using Maybe I'd rewrite the whole thing like this:
const or = (a, b) => {
return Maybe.of(a == null ? b : a);
}
And then I would just pass in the property accesses:
const obj1 = { a: 2, c: 3 };
const obj2 = { b: 4 };
const prop = "a"
const result = or(obj1["prop"], obj2["prop"]); // Maybe(2)
Update
Credit to #Bergi in the comments reminding me about Alternative. You could add a method to the Maybe class above like so:
alt (x) {
if (!(x instanceof Maybe)) {
throw new TypeError("Expected a Maybe");
}
return this.chain(x => x) == null ? x : this;
}
// semantics
Maybe.of(null).alt(Maybe.of(3)); // Maybe(3)
Maybe.of(2).alt(Maybe.of(4)); // Maybe(2)
// usage
Maybe.of(obj1[prop]).alt(Maybe.of(obj2[prop]));
Note that this doesn't totally satisfy as an implementation of Alternative (you'd also need a zero/empty method) but you can read here and here for more details. This is probably the best replacement for the function you posted.
I'm wondering if there's a concise or specific way to access values in the middle of an FP chain in JavaScript. Example:
const somestuff = [true, true, false];
let filteredCount = 0;
somestuff.filter((val) => val)
.forEach((val) => console.log(val));
Above, I'd like to set filteredCount to the length of the array returned by the filter function. The most straight-forward way is:
const somestuff = [true, true, false];
const filteredStuff = somestuff.filter((val) => val);
let filteredCount = filteredStuff.length;
filteredStuff.forEach((val) => console.log(val));
This is certainly valid but it breaks our FP chain and introduces an additional holding variable. I'm wondering if there's a convention for accessing values in the middle of the chain. Something like .once() that runs once and implicitly returns the value passed in, but nothing like that exists.
For debugging, I often use a function called tap to temporarily add a side-effect (like your console.log) to a function:
const tap = f => x => (f(x), x);
This function returns whatever it is passed, but not before calling another function with the value. For example:
const tap = f => x => (f(x), x);
const tapLog = tap(console.log);
const x = tapLog(10);
console.log("x is", x);
Your snippet basically does this:
Filter a list
(log the list)
Retrieve a length property from an array
If you construct this function using pipe or compose, you can "inject" the console.log in between without interrupting the data flow:
const countTrues = pipe(
filter(isTrue),
prop("length")
);
const countTruesWithLog = pipe(
filter(isTrue),
tap(console.log),
prop("length")
);
In a snippet:
// Utils
const isTrue = x => x === true;
const prop = k => obj => obj[k];
const tap = f => x => (f(x), x);
const filter = f => xs => xs.filter(f);
const pipe = (...fns) => x => fns.reduce((res, f) => f(res), x);
// Logic:
// Filter an array using the isTrue function
// and return the length of the result
const countTrues = pipe(
filter(isTrue),
prop("length")
);
// Create a filter with a console.log side-effect
// and return the length of the result
const countTruesWithLog = pipe(
filter(isTrue),
tap(console.log),
prop("length")
);
// App:
const somestuff = [true, true, false];
console.log("pure:");
const countA = countTrues(somestuff)
console.log(countA);
console.log("with log:")
const countB = countTruesWithLog(somestuff);
console.log(countB);
The reason there's no Array.prototype method like that, is that it has a side effect. This is something that is specifically avoided in functional programming.
However if you don't care about writing 'Pure Functions', or even the functional paradigm, you could put the side effect in your callbacks, or write a function in the Array prototype.
ie.
Array.prototype.once = function(callback) {
callback(this)
return this
}
You also have other hacky options like in the other answer
I don't think there's something like that by default. What you can do is extend Array, but I'm not really fond of extending framework classes (clashes with other once implementations for example). In this case you'd end up with:
Array.prototype.once = function once(func) {
func(this);
return this;
}
which is called like:
var filteredStuff = somestuff
.filter((val) => val)
.once(function(array) {
console.log(array.length);
})
.forEach((val) => console.log(val));
On the other hand, you can try to use default functions. One of these function that can access all items at once is reduce. Define a function once, that will call its first parameter once (:)) and you'd end up with something like:
function once(func) {
return function(accumulator, currentValue, currentIndex, array) {
if(currentIndex === 1) {
func(array);
}
return array;
}
}
which you'd be able to call like this:
var filteredStuff = somestuff
.filter((val) => val)
.reduce(once(function(array) {
console.log(array.length);
}), [0])
.forEach((val) => console.log(val));
Notice the ugly [0] to ensure once calls the passed function at least once (empty array included).
Both solutions aren't too neat, but it's the best I can come up with given the criteria.
I want to debounce a stream - but only if the source value is the same as before. How would I do this with RxJS 5?
I do not want to emit a value if the value is the same and I emitted it previously within a specified time window. I should be able to use the value from the stream - or compare function similar to distinctUntilChanged.
It depends on what you're trying to do; I came upon this question when I was trying to do something similar, basically debouncing but with different debounces for different values of an object.
After trying the solution from jayphelps I couldn't get it to behave as I wanted. After much back and forth, turns out there is an in built easy way to do it: groupby.
const priceUpdates = [
{bid: 10, id: 25},
{bid: 20, id: 30},
{bid: 11, id: 25},
{bid: 21, id: 30},
{bid: 25, id: 30}
];//emit each person
const source = Rx.Observable.from(priceUpdates);
//group by age
const example = source
.groupBy(bid => bid.id)
.mergeMap(group$ => group$.debounceTime(500))
const subscribe = example.subscribe(val => console.log(val));
Output:
[object Object] {
bid: 11,
id: 25
}
[object Object] {
bid: 25,
id: 30
}
Jsbin: http://jsbin.com/savahivege/edit?js,console
This code will group by the bid ID and debounce on that, so therefore only send the last values for each.
I'm not aware of any way to do this with without creating your own operator because you need to maintain some sort of state (the last seen value).
One way looks something like this:
// I named this debounceDistinctUntilChanged but that might not be
// the best name. Name it whatever you think makes sense!
function debounceDistinctUntilChanged(delay) {
const source$ = this;
return new Observable(observer => {
// Using an object as the default value
// so that the first time we check it
// if its the same its guaranteed to be false
// because every object has a different identity.
// Can't use null or undefined because source may
// emit these!
let lastSeen = {};
return source$
.debounce(value => {
// If the last value has the same identity we'll
// actually debounce
if (value === lastSeen) {
return Observable.timer(delay);
} else {
lastSeen = value;
// This will complete() right away so we don't actually debounce/buffer
// it at all
return Observable.empty();
}
})
.subscribe(observer);
});
}
Now that you see an implementation you may (or may not) find it differs from your expectations. Your description actually left out certain details, like if it should only be the last value you keep during the debounce time frame or if it's a set--basically distinctUntilChanged vs. distinct. I assumed the later.
Either way hopefully this gives you a starting point and reveals how easy it is to create custom operators. The built in operators definitely do not provide solutions for everything as-is, so any sufficiently advanced app will need to make their own (or do the imperative stuff inline without abstracting it, which is fine too).
You can then use this operator by putting it on the Observable prototype:
Observable.prototype.debounceDistinctUntilChanged = debounceDistinctUntilChanged;
// later
source$
.debounceDistinctUntilChanged(400)
.subscribe(d => console.log(d));
Or by using let:
// later
source$
.let(source$ => debounceDistinctUntilChanged.call($source, 400))
.subscribe(d => console.log(d));
If you can, I recommend truly understanding what my code does, so that in the future you are able to easily make your own solutions.
Providing an answer for RxJS 6+ with the method suggested by #samberic in an ngrx effect to group actions coming from a same source id with RxJS 6.
this.actions$.pipe(
ofType(actionFoo, actionBar), // Two different ngrx action with an id property
groupBy(action => action.id), // Group by the id from the source
mergeMap(action => action.pipe(
debounceTime(5000)
))
).pipe(
// Do whatever it is that your effect is supposed to do!
)
Here is my RXJS 6+ version in typescript that works 100% as originally requested. Debounce (restart timer) on every new source value. Emit value only if the new value is different from the previous value or the debounce time has expired.
// custom rxjs operator to debounce while the source emits the same values
debounceDistinct<T>(delay: number) {
return (source: Observable<T>): Observable<T> => {
return new Observable(subscriber => {
let hasValue = false;
let lastValue: T | null = null;
let durationSub: Subscription = null;
const emit = () => {
durationSub?.unsubscribe();
durationSub = null;
if (hasValue) {
// We have a value! Free up memory first, then emit the value.
hasValue = false;
const value = lastValue!;
lastValue = null;
subscriber.next(value);
}
};
return source.subscribe(
(value: T) => {
// new value received cancel timer
durationSub?.unsubscribe();
// emit lastValue if the value has changed
if (hasValue && value !== lastValue) {
const value = lastValue!;
subscriber.next(value);
}
hasValue = true;
lastValue = value;
// restart timer
durationSub = timer(delay).subscribe(() => {
emit();
});
},
error => {
},
() => {
emit();
subscriber.complete();
lastValue = null;
});
});
}
}
Another possibility, not sure if supported with rxjs5 though:
source$.pipe(
pairwise(),
debounce(([a, b]) => {
if (a === b) {
return interval(1000)
}
return of();
}),
map(([a,b]) => b)
)
.subscribe(console.log);
https://stackblitz.com/edit/typescript-39nq7f?file=index.ts&devtoolsheight=50
update for rxjs 6 :
source$
.pipe(
// debounceTime(300), optionally un-comment this to add debounce
distinctUntilChanged(),
)
.subscribe(v => console.log(v))
This rxjs6+ operator will emit when the source 'value' has changed or when some 'delay' time has passed since last emit (even if 'value' has not changed):
export function throttleUntilChanged(delay: number) {
return (source: Observable<any>) => {
return new Observable(observer => {
let lastSeen = {};
let lastSeenTime = 0;
return source
.pipe(
flatMap((value: any) => {
const now = Date.now();
if (value === lastSeen && (now - lastSeenTime) < delay ) {
return empty();
} else {
lastSeen = value;
lastSeenTime = now;
return of(value);
}
})
)
.subscribe(observer);
});
};
}