I'm wondering why shouldComponentUpdate by default does return true when it seems to me like the 1 and only reason not to re-render is if nextProps and this.props are "equal" in the sense of a deep comparison like
function shouldWeRender(prev, next)
{
return !objectsEqual(prev, next);
}
function objectsEqual(a, b)
{
var ta = typeof(a), tb = typeof(b);
if(ta !== 'object' || tb !== 'object') {
throw 'Arguments must be objects';
}
var ka = _.keys(a), kb = _.keys(b);
if(ka.length !== kb.length) {
return false;
}
_.forEach(ka, function(key) {
if(!b[key]) {
return false;
}
else if(typeof(a[key] === 'object') && !objectsEqual(a[key], b[key])) {
return false;
}
else if(a[key] !== b[key]) {
return false;
}
});
return true;
}
Shouldn't that be able to work as universal condition for rendering, assuming that we are using stateless components as is proper?
Related
I have solved the problem Count the smiley faces:
Given an array (arr) as an argument complete the function countSmileys that should return the total number of smiling faces.
Rules for a smiling face:
Each smiley face must contain a valid pair of eyes. Eyes can be marked as : or ;
A smiley face can have a nose but it does not have to. Valid characters for a nose are - or ~
Every smiling face must have a smiling mouth that should be marked with either ) or D
No additional characters are allowed except for those mentioned.
Valid smiley face examples: :) :D ;-D :~)
Invalid smiley faces: ;( :> :} :]
Example
countSmileys([':)', ';(', ';}', ':-D']); // should return 2;
countSmileys([';D', ':-(', ':-)', ';~)']); // should return 3;
countSmileys([';]', ':[', ';*', ':$', ';-D']); // should return 1;
Note
In case of an empty array return 0. You will not be tested with invalid input (input will always be an array). Order of the face (eyes, nose, mouth) elements will always be the same.
Then when I look through the solutions I find that many people use regexp. Then I want write a state machine to implement regexp and solve this problem. But I failed. This is my code:
function countSmileys(smileys) {
let state = smileyHasValidEye;
return smileys.filter(smiley => {
for (let s of [...smiley]) {
state = state(s);
}
return state === true;
}).length;
}
function smileyHasValidEye(s) {
if (s === ':' || s === ';') {
return smileyHasValidNose;
}
return smileyHasValidEye;
}
function smileyHasValidNose(s) {
if (s === '-' || s === '~') {
return smileyHasValidMouth;
}
return smileyHasValidMouth(s);
}
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return;
}
console.log(countSmileys([':)', ';(', ';}', ':-D']));
And the error I get is:
state = state(s);
^
TypeError: state is not a function
Then I debugged my code I found the procedure doesn't enter the smileyHasValidNose function. Then I don't know the reason.
The problem is you don't really reset state in between smileys. So the next smiley state will be true which you can't call (it's not a function).
You could use a local variable for state that resets it to the first function (the first step).
function countSmileys(smileys) {
let firstStep = smileyHasValidEye;
return smileys.filter(smiley => {
let state = firstStep;
for (let s of [...smiley]) {
state = state(s);
}
return state === true;
}).length;
}
function smileyHasValidEye(s) {
if (s === ':' || s === ';') {
return smileyHasValidNose;
}
return smileyHasValidEye;
}
function smileyHasValidNose(s) {
if (s === '-' || s === '~') {
return smileyHasValidMouth;
}
return smileyHasValidMouth(s);
}
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return;
}
console.log(countSmileys([':)', ';(', ';}', ':-D']));
This code however, will error if there's more on the string besides the smiley (or a partial of the smiley).
I would change smileyHasValidMouth to return false if it doesn't detect a smiley. Just to be more consistent here...
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return false;
}
And adjust your loop to exit early if it finds a value that is not a function.
for (let s of [...smiley]) {
state = state(s);
if(typeof state !== 'function') return state;
}
function countSmileys(smileys) {
let firstStep = smileyHasValidEye;
return smileys.filter(smiley => {
let state = firstStep;
for (let s of [...smiley]) {
state = state(s);
if (typeof state !== 'function') return state;
}
}).length;
}
function smileyHasValidEye(s) {
if (s === ':' || s === ';') {
return smileyHasValidNose;
}
return smileyHasValidEye;
}
function smileyHasValidNose(s) {
if (s === '-' || s === '~') {
return smileyHasValidMouth;
}
return smileyHasValidMouth(s);
}
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return false;
}
console.log(countSmileys([':~(', ':>', ':D', ':(', ':o>', ';)', ':)']));
I know that below two getters are duplicates and could be consolidated and written in a better way. Could any one please help me come up with a way to consolidate these:-
isEqual here is a lodash library to compare two objects.
state in here is an injected state which I am picking the objects from.
public get isUpperModified(): boolean {
if (!this.isUpperAvailable) {
return false;
}
if (
(this.orders.upperPreference.type === '1' &&
this.state.fetchedData.upperPreference.type === '1') ||
(this.orders.upperPreference.type === 'UPPER' &&
this.state.fetchedData.upperPreference.type === 'UPPER')
) {
return false;
}
if (!isEqual(this.orders.upperPreference, this.state.fetchedData.upperPreference)) {
return true;
}
return false;
}
public get isLowerModified(): boolean {
if (!this.isLowerAvailable) {
return false;
}
if (
(this.orders.lowerPreference.type === '1' &&
this.state.fetchedData.lowerPreference.type === '1') ||
(this.orders.lowerPreference.type === 'LOWER' &&
this.state.fetchedData.lowerPreference.type === 'LOWER')
) {
return false;
}
if (!isEqual(this.orders.lowerPreference, this.state.fetchedData.lowerPreference)) {
return true;
}
return false;
}
There are more than 1 way to achieve this.
You can create a new function isModified(type: string) and pass upper or lower as an argument.
Hope this helps
public get isUpperModified(): boolean {
return this.isModified('upper');
}
public get isLowerModified(): boolean {
return this.isModified('lower');
}
private isModified(type: 'lower' | 'upper'): boolean {
const available = type === 'lower' ? this.isLowerAvailable : this.isUpperAvailable;
const order = type === 'lower' ? this.orders.lowerPreference : this.orders.upperPreference;
const state = type === 'lower' ? this.state.fetchedData.lowerPreference : this.state.fetchedData.upperPreference;
if (!available) {
return false;
}
if (
(order.type === '1' &&
state.type === '1') ||
(order.type === type.toUpperCase() &&
state.type === type.toUpperCase())
) {
return false;
}
if (!isEqual(order, state)) {
return true;
}
return false;
}
I would do it something like this
public get isModified(type: 'lower' | 'upper'): boolean {
const isAvailable = type === "lower" ? this.isLowerAvailable : this.isUpperAvailable
const preference = type === "lower" ? "lowerPreference" : "upperPreference";
if (!isAvailable) {
return false;
}
if (
(this.orders[preference].type === '1' &&
this.state.fetchedData[preference].type === '1') ||
(this.orders[preference].type === 'LOWER' &&
this.state.fetchedData[preference].type === 'LOWER')
) {
return false;
}
if (!isEqual(this.orders[preference], this.state.fetchedData[preference])) {
return true;
}
return false;
}
Then while calling this method
use isModified("upper") instead of isUpperModified
and
use isModified("lower") instead of isLowerModified
How can I pass the function logic to the find-function in order to use it on each document?
I am trying to find Mongo-documents recursively with certain values in a property. I have defined an "algorithm" and would like to this to the find-function, but despite reading about queries, searching and find(), I have had no luck.
// match every node with propertyname "Reference" with the exact value "43_1"
var logic = function(currentKey, currentValue) {
return ( currentKey == "Reference" && currentValue === "43_1" );
};
db.getCollection('metavalg').find(function() {
function recurSearchObj(doc) {
return Object.keys(doc).some(function(key) {
if ( typeof(doc[key]) == "object" && doc[key] !== null ) {
return recurSearchObj(doc[key]);
} else {
// invoke matching-logic
return logic(key, doc[key]);
}
});
}
// search document recursively
return recurSearchObj(this);
})
With the help of Vladimir M, I ended up rearringing the code and doing this:
db.getCollection('metavalg').find(function() {
function inspectObj(doc, logic) {
return Object.keys(doc).some(function(key) {
if ( typeof(doc[key]) == "object" && doc[key] !== null ) {
return inspectObj(doc[key], logic);
} else {
return logic(key, doc[key]);
}
});
}
return inspectObj(this, function(currentKey, currentValue) {
return ( currentKey == "Nummer" && currentValue === "43_1" );
//return ( currentKey == "Navn" && /max\.? 36/i.test(currentValue) );
});
})
You can pass logic method as a parameter to recurcive search object:
// match every node with propertyname "Reference" with the exact value "43_1"
var logic = function(currentKey, currentValue) {
return ( currentKey == "Reference" && currentValue === "43_1" );
};
db.getCollection('metavalg').find(function() {
function recurSearchObj(doc, aLogic) {
return Object.keys(doc).some(function(key) {
if ( typeof(doc[key]) == "object" && doc[key] !== null ) {
return recurSearchObj(doc[key], aLogic);
} else {
// invoke matching-logic
if( aLogic(key, doc[key]) ){
// then do somthing
}
}
});
}
// search document recursively
return recurSearchObj(this, logic);
});
I have a function like so:
function contains(needle,haystack) {
return $.each(haystack,function(i,v) {
if(v == needle) {
console.log('true, so return');
return true;
}
if(typeof v == 'object') {
return app.contains(needle,v);
}
console.log('I have not yet returned');
return false;
});
}
When I pass something into it (let's say an object of length 2, with one item matching) the console shows:
true, so return
I have not yet returned
I have not yet returned
Why is this? How do I fix it?
Returning true/undefined from the each callback will continue executing the next iteration.
What you need to do is to use a flag and if a match is found then set the flag to true and return false so that rest of the iterations will be ignored
function contains(needle, haystack) {
var result = false;
$.each(haystack, function (i, v) {
if (v == needle) {
console.log('true, so return');
result = true;
return false;
}
if (typeof v == 'object') {
if (app.contains(needle, v)) {
result = true;
return false;
}
}
console.log('I have not yet returned');
});
return result;
}
It is because you return only from inner function of each $.each iteration.
To break $.each loop you need return false, so return !result will return false if and only if result if found
function contains(needle,haystack) {
var result = false;
$.each(haystack,function(i,v) {
if(v == needle) {
result = true;
return !result;
}
if(typeof v == 'object') {
result = contains(needle,v);
return !result;
}
});
return result;
}
http://jsfiddle.net/8dggq7ds/2/
Or shorter version
function contains(needle,haystack) {
var result = false;
$.each(haystack,function(i,v) {
return !(result = (typeof v === 'object') ? contains(needle,v) : v == needle);
});
return result;
}
Moreover, recursion call app.contains() change to contains().
How would I go about creating a recursive function that can search through a list for a node where x = 10?
I do not have any javascript experience so I am not sure where to start so would appreciate and input
I have come across the following code and tried to adapt it but I am not sure if I am on the right track:
function search(_for, _in) {
var r;
for (var p in _in) {
if ( p === 10 ) {
return _in[p];
}
if ( typeof _in[p] === 'object' ) {
if ( (r = search(_for, _in[p])) !== null ) {
return r;
}
}
}
return null;
}
Thank you in advance
Try this
var finder = function(needle, haystack) {
if (haystack.length == 0)
return false
if (haystack[0] == needle)
return true
return finder(pin, haystack.slice(1))
}
Or
var finder = function(pin, haystack) {
return (haystack[0] == pin) || (haystack.length != 0) && finder(pin, haystack.slice(1))
}
Recursion FTW