I have the code shown below for destructuring end_time property from this.props.auction object
const {auction: {auction: {end_time}}} = this.props;
But the issue here is above constant will be undefined if auction is an empty object. To fix this, I have changed the code to
if(Object.keys(this.props.auction).length) {
var {auction: {auction: {end_time}}} = this.props;
} else {
var {end_time} = "";
}
The above solution works but is ugly and I believe there is definitely a far better way of doing it.
Followed the answer from this post and
My attempt so far is:
const {auction: {auction: {end_time = null}}} = this.props || {};
I thought the above one will set end_time by default to null but I guess since auction is not defined it is throwing an error.
Please suggest a better way of declaring the end_time constant which takes care of an empty auction
You don’t need to use destructuring every time you can use it.
const auction = this.props.auction.auction;
const end_time = auction === undefined ? null : auction.end_time;
You could potentially use destructuring with default values like this:
const { auction: { auction: { end_time = null } = {} } = {} } = this.props || {};
But because the syntax above is cumbersome and unnatural to follow, I ultimately yield to Ry's advice in this case:
You don’t need to use destructuring every time you can use it.
I realize this is tagged ecmascript-6, but this question presents a great example where using the optional chaining operator and nullish coalescing operator seems the most natural solution, at least when they become officially merged into the ECMAScript 2020 specification:
const end_time = this.props?.auction?.auction?.end_time ?? null;
By combining the use of Optional chaining and Nullish Coalescing Operator you can achieve this goal with only one line like this:
const end_time = props.auction?.auction?.end_time ?? '';
Below are few testing functions to understand the concept:
const end_time_defaultValue = 'end_time_defaultValue';
function testWithEndTime() {
const props = {
auction: {
auction: {
end_time: new Date(),
kay1: 'value1',
kay2: 'value2'
}
}
};
const end_time = props.auction?.auction?.end_time ?? end_time_defaultValue;
console.log('testWithEndTime() => ', end_time);
}
testWithEndTime();
// outputs the today date
function testWithoutEndTime() {
const props = {
auction: {
auction: {
kay1: 'value1',
kay2: 'value2'
}
}
};
const end_time = props.auction?.auction?.end_time ?? end_time_defaultValue;
console.log('testWithoutEndTime() => ', end_time);
}
testWithoutEndTime();
// outputs the end_time_defaultValue
// because the key 'end_time' does not exist
function testWithoutAuctionAuction() {
const props = {
auction: {
}
};
const end_time = props.auction?.auction?.end_time ?? end_time_defaultValue;
console.log('testWithoutAuctionAuction() => ', end_time);
}
testWithoutAuctionAuction();
// outputs the end_time_defaultValue
// because the key 'auction.auction' does not exist
function testWithoutPropsAuction() {
const props = {};;
const end_time = props.auction?.auction?.end_time ?? end_time_defaultValue;
console.log('testWithoutPropsAuction() => ', end_time);
}
testWithoutPropsAuction();
// outputs the end_time_defaultValue
// because the key 'props.auction' does not exist
Be careful about browsers compatibility
But if you're using a framework like React, babel will do the job for you.
You may do de-structuring in 2 steps: first de-structure your props, then the necessary object that could be undefined at certain point in component life-cycle.
// instead of
const {auction: {auction: {end_time}}} = this.props;
// you may do
const { auction } = this.props;
let end_time;
if(auction){
({ end_time } = auction);
}
Related
Is there way to change the index dynamically? or rebuild this object to where the 1 will be the Id of what ever object get passed into the function? Hope this makes sense.
export const createTree = (parentObj) => {
//keep in memory reference for later
const flatlist = { 1: parentObj }; <---- change the 1 based on parent.Id
...
}
My attempt thinking it would be easy as:
const flatlist = { parentObj.Id: parentObj };
Use computed property names to create a key from an expression:
const createTree = (parentObj) => {
const flatlist = { [parentObj.id]: parentObj };
return flatlist;
}
console.log(createTree({ id: 1 }));
I am working with a very simple if statement tree and want to write the code as DRY as possible, I believe I am implementing the DRYest code possible for my use case and have attempted to write in pointers to an object key (doesn't seem possible without setter function) and simplify the curly brackets but for purposes of the question I have left them in to make things clear on what needs to be done.
Is there a simpler version of this if tree?
let query = {};
if (min_budget || max_budget) {
if(min_budget && max_budget) {
query['budget.middleBound'] = { $gte: min_budget, $lte: max_budget }
} else if (min_budget && !max_budget) {
query['budget.middleBound'] = { $gte: min_budget }
} else if (max_budget && !min_budget) {
query['budget.middleBound'] = { $lte: max_budget }
}
}
Consolidating the various comments:
let query = {};
if (min_budget && max_budget) {
query['budget.middleBound'] = { $gte: min_budget, $lte: max_budget };
} else if (min_budget) { // ***
query['budget.middleBound'] = { $gte: min_budget };
} else if (max_budget) { // ***
query['budget.middleBound'] = { $lte: max_budget };
}
Barring more domain-specific information, that's probably the simple, direct version.
You could do it with just two ifs if you don't mind modifying an existing object:
let query = {};
if (min_budget || max_budget) {
const mb = query['budget.middleBound'] = {};
if (min_budget) {
mb.$gte = min_budget;
}
if (max_budget) {
mb.$lte = max_budget;
}
}
If you only care about syntax and short code, you could make use of the short-circuit evaluation.
let query = {};
let bound = (min_budget || max_budget) && (query['budget.middleBound'] = {});
min_budget && (bound.$gte = min_budget);
max_budget && (bound.$lte = max_budget);
The idea of the code is that we first create a new Object at query['budget.middleBound'] if we need to add either the min or max conditions.
We also save a reference in bound and use it further (so we don't have to access again the budget.middleBound property on query, which has a long name). We still only create one extra Object if needed.
Note that adding properties to Objects after you create them is slower than creating the Object with all the keys already present.
It's not usually recommended to write code like this in production, as it takes more time to understand what the code does when executed.
I am wondering to see if there is a way to destructure objects in javascript with using a variable. Where as I was doing something like this in my function -
mutateTaxon(data) {
const { content } = data;
const { plp } = content || {};
...
This was working fine, however I need to expand this function based off another factor that can change if I need to use data.content (which it is using now) or data.collection. So I have another node on the data - which changes call to call. I am trying something like this -
mutateTaxon(data) {
const match = lowerCase(data.taxonomy.name);
const { match } = data;
const { plp } = match || {};
Where that match variable would evaluate to either content or collection (as expected). This does not seem to work however, maybe it is not possible? I was thinking maybe the match var needed to be evaluated so I tried something like -
const { [[match]] } = data;
which also is not working. Maybe this is not possible, or I am approaching this wrong. I am wondering, is something like this possible? Thanks!
The destructuring syntax would, as Jonas W. said, be a bit more cumbersome than the bracket notation, but nonetheless, this is how you would do it:
mutateTaxon(data) {
const key = lowerCase(data.taxonomy.name);
const { [key]: { plp } = {} } = data;
Demo
const foo = { bar: { plp: 'success' } }
const key = 'bar'
const { [key]: { plp } = {} } = foo
console.log(plp)
And to confirm that default parameter = {} works as expected:
const foo = { }
const key = 'bar'
const { [key]: { plp } = {} } = foo
console.log(plp)
const key = lowerCase(data.taxonomy.name);
const match = data[key];
I dont think that object destructuring is useful here. But if you need that:
const key = lowerCase(data.taxonomy.name);
const {[key]: match} = data;
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>
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);
});
};
}