I got used to using bind to remember the last result of function and to keep track to be able to use the last result for the next result. For instance to concat or join last string to a new string without using outer variables:
function remStr(outStr){
return function c(lastStr,newStr){
if(!newStr)return lastStr;
var all = lastStr+newStr;
return c.bind(null,all);
}.bind(null,outStr);
}
var str = remStr('stack');
str = str('over');
str = str('flow');
str(); // stackoverflow
The problem is that I want to call remStr several times and so bind came into play. But can it be done better or just differently, maybe it turns out that for one case an approach fulfills a task better than remStr?
If I understand your intention correctly, how about just using the closure?
function remStr(outStr) {
return function c(newStr) {
if (!newStr) return outStr;
outStr += newStr;
return c;
}
}
var str = remStr('stack');
str = str('over');
str = str('flow');
str(); // stackoverflow
As mentioned by Tomalak in the comments, JavaScript strings are immutable, so if you intend to use large or many strings, you will probably want to buffer them in an array.
function remStr(outStr) {
var buffer = [outStr || ''];
return function c(newStr) {
if (!newStr) return buffer.join('');
buffer.push(newStr);
return c;
}
}
var str = remStr('stack');
str = str('over');
str = str('flow');
str(); // stackoverflow
You shouldn't be using Function.bind here at all. You can cache the arguments. And then join it.
This approach is widely known as functions are also objects and can have properties. Function.bind is used to change the context of the given function and that isn't what we want.
function concat(word){
return function fn(anWord){
if(!anWord) return fn.words.join("");
(fn.words || (fn.words = [word])).push(anWord);
}
}
Now you can use it like below:
var str = concat("stack");
str("over");
str("flow");
console.log(str()); // "stackoverflow"
Related
I need to use a function like this for multiple times with different ids based on different conditions (e.g. select values). I don't want to write a different function for every id because there will be lots of them in the future.
var getValue = function(id){
var Element = document.getElementById(id);
if(Element){
if(Element.value){
return " " + Element.value;
}
return "";
}
return "";
}
I have an array including some other functions:
FunctionList = [ SomeFunction, SomeFunction2 ];
Based on some conditions, I add getValue function with different parameters to this array.
FunctionList.push(getValue(SomeId1));
FunctionList.push(getValue(SomeId2));
FunctionList.push(getValue(SomeOtherId));
In the end, I need to add outputs of my functions (which are strings) to another string.
var updateCode = function(){
var code = CodeHeader;
for (var i=0; i<FunctionList.length; i++){
code += FunctionList[i]();
}
return code;
}
But I am unable to use it like this since some items of array are not function names now.
I like pure js solutions, but if it's not possible I can go for jQuery or so.
You can use bind to create such a tailored function on the fly:
FunctionList.push(getValue.bind(null, SomeId1));
The first argument (null here) will act as this during function execution.
This differs from what you had:
FunctionList.push(getValue(SomeId1));
...as here you don't push a function, but the result of a function execution. bind however, will not execute the function, but create a new one from the given function.
Alternative
In your example you call the same function with different arguments. If that is always the case, you could also go for an array of only arguments:
argumentList.push(SomeId1); // Not the function, just the argument
// ...etc
And then updateCode would become:
var updateCode = function(){
var code = CodeHeader;
for (var i=0; i<argumentList.length; i++){
code += getValue(argumentList[i]);
}
return code;
}
... or shorter by using map:
var updateCode = () => CodeHeader + argumentList.map(getValue).join("");
As in your getValue function, you prefix the returned value with a space, you could omit that prefix, and just use .join(" ") in the above one-liner. The downside is that it would not deal the same way with empty return values.
I want to create a function "palindromes()" which checks whether a value is a palindrome (spelled the same forwards and backwards).
In order to do that, I have created 4 functions, which:
Makes all letters small
Removes all non-letter characters
Reverses the ensuing array, and finally...
Checks whether that array is a palindrome.
See functions bellow:
function makeSmall(input) {
lowerCase = input.toLowerCase();
return lowerCase;
}
function keepOnlyLetters(input) {
var patt1 = /[a-z]/g;
var onlyLetters = input.match(patt1);
return onlyLetters;
}
function reverseArray(array) {
var reversedArray = array.slice().reverse();
return reversedArray;
}
function checkPalindromes(array) {
var reversedArray = array.slice().reverse();
for (let i = 0; i <= array.length; i++) {
if (array[i] != reversedArray[i]) {
return false;
}
}
return true;
}
How do I make sure that the function "palindromes()" takes one value and runs it through all these functions to finally give me an answer (true or false) of whether that value is a palindrome or not?
Best regards,
Beni
There's a point of diminishing returns with functions. When calling the function is just as short as using the body of the function inline, you've probably hit that point. For example, makeSmall(input) is really no improvement to just using input.toLowerCase() inline and will be slower and harder to understand. input.toLowerCase() is already a function; it's just wasted work to wrap it in another function.
Having said that, to answer your question, since all your functions return the value that's input to the next, you can put you functions in an array and call reduce():
function palindromes(input) {
return [makeSmall, keepOnlyLetters, reverseArray, checkPalindromes].reduce((a, c) => c(a), input)
}
So first before trying to do composition at first it sometimes works best to do it sequentially to make sure you understand the problem. As you get better at composition eventually you'll know what tools to use.
function checkPalindrome(string){
return string
.toLowerCase()
.match(/[a-z]/g)
.reverse()
.reduce(function ( acc, letter, index ) {
return acc && string[index] == letter
})
}
checkPalindrome('test') // false
checkPalindrome('tet') // true
Okay good we understand it procedurally and know that there are four steps. We could split those four steps out, however since two steps require previous knowledge of the array state and we don't want to introduce converge or lift just yet we should instead just use a pipe function and combine the steps that require a previous state. The reason for that is eventually functions just lose how much smaller you can make them, and attempting to split those steps up not only hurts readability but maintainability. Those are not good returns on the effort invested to make two functions for that part!
function pipe (...fns){
return fns.reduce( function (f, g){
return function (...args){
return g(
f(...args)
)
}
}
}
All this function does it it pre-loads(composes) a bunch of functions together to make it so that the output of one function applies to the input of the next function in a left to right order(also known as array order).
Now we just need out three functions to pipe:
function bringDown(string){ return string.toLowerCase() } // ussually called toLower, see note
function onlyLetters(string){ return string.match(/[a-z]/g) }
function flipItAndReverseItCompare(arrayLike){ // I like missy elliot... ok?
let original = Array.from(arrayLike)
return original
.slice()
.reverse()
.reduce(function (acc, val, ind){
return acc && val == original[ind]
})
}
Now we can just pipe them
let palindrome = pipe(
bringDown,
onlyLetters,
flipItAndReverseItCompare
)
!palindrome('Missy Elliot') // true... and I never will be
palindrome('Te t') // true
Now you're well on your way to learning about function composition!
You can just string the function calls together like this...
var input = 'Racecar';
if (checkPalindromes(reverseArray(keepOnlyLetters(makeSmall(input))))) {
alert("It's a palindrome");
}
You can just call them in a nested fashion and return the final result in your palindrome function.
Sample Code: (with changes indicated in the comments)
function makeSmall(input) {
// Added var to prevent it from being a global
var lowerCase = input.toLowerCase();
return lowerCase;
}
function keepOnlyLetters(input) {
var patt1 = /[a-z]/g;
var onlyLetters = input.match(patt1);
return onlyLetters;
}
// This function is not really needed and is unused
/*function reverseArray(array) {
var reversedArray = array.slice().reverse();
return reversedArray;
}*/
function checkPalindromes(array) {
var reversedArray = array.slice().reverse();
for (let i = 0; i <= array.length; i++) {
if (array[i] != reversedArray[i]) {
return false;
}
}
return true;
}
// New Palindromes function
function palindromes(input){
return checkPalindromes(keepOnlyLetters(makeSmall(input)));
}
Note:
You don't really need so many functions to do this. I'm putting this here as a strict answer to your exact question. Other answers here show how you can solve this in shorter (and better?) ways
try the following snippet.
function makeSmall(input) {
lowerCase = input.toLowerCase();
return lowerCase;
}
function keepOnlyLetters(input) {
var patt1 = /[a-z]/g;
var onlyLetters = input.match(patt1);
return onlyLetters;
}
function reverseArray(array) {
var reversedArray = array.slice().reverse();
return reversedArray;
}
function checkPalindromes(array) {
var reversedArray = array.slice().reverse();
for (let i = 0; i <= array.length; i++) {
if (array[i] != reversedArray[i]) {
return false;
}
}
return true;
}
var result = checkPalindromes(reverseArray(keepOnlyLetters(makeSmall("Eva, Can I Stab Bats In A Cave"))));
console.log(result);
Notice how functions are called one after the other in one line.
I'm trying to evaluate this string:
var proposition = "(true&&false)⇒!true";
console.log(eval(proposition));
but that does not work because this "⇒" character, that is an unrecognized logic connective.
I was reading this answer post Boolean Implication but I still can't apply that to what I need.
I have this two functions that returns the implication and biconditional boolean result between two booleans values:
function Implication(v1,v2){
return (!v1)||v2; //implication formula
}
function Biconditional(v1,v2){
if(v1==v2) return true; //biconditional formula
else return false;
}
And I can apply that functions for a specific logical proposition like this:
var proposition = "(true&&false)⇒!true";
var pars = proposition.split("⇒")
var result = Implication(eval(pars[0]),eval(pars[1]))
console.log(result)
And that works, but what if I have something like: "((true⇔true)&&false)⇒false⇔true||false" or any other convination?
I've got a function that wants to access a global variable, the name of which arrives as a string argument. This is how it looks now, using eval:
function echoVar(whichone){
document.write(eval(whichone));
}
I thought I'd just use the window[] syntax and have this:
function echoVar(whichone) {
document.write(window[whichone]);
}
If I create a var and call it like this, it doc writes ABC as expected:
var abc = "ABC";
echoVar("abc");
If the var I want to access is an array element though, it doesn't work:
var def = ["DEF"];
echoVar("def[0]"); //fails with undefined
Obviously that's actually executing window[def[0]] which rightly gives undefined (because there's no variable called DEF). What I actually want to happen is that it executes window["def"][0].
The only way I know to achieve this, is to do a split on the whichone parameter with "[" as the delimiter and then use the split [0] as the window index and a parseInt on split [1] to get the index, like this:
function echoVar(whichone){
if(whichone.indexOf("[")==-1){
document.write(window[whichone]);
}
else{
var s = whichone.split("[");
var nam = s[0];
var idx = parseInt(s[1]);
document.write( window[nam][idx] );
}
}
Am I overlooking something obvious? I'd rather keep the eval than have to do all that.
If you dislike using eval in your code, you can always do this:
function echoVar(whichone) {
document.write(Function("return " + whichone)());
}
Unless this is some sick experiment, you should never be writing Javascript code that looks like this. This is terrible; you need to re-think your design. I know this isn't what you're looking for, but it's the right answer.
The fact is you've got a piece of a javscript expression in a string so you either have to parse it yourself or use eval to parse it for you unless you change the way it's passed like this:
function echoVar(a,b) {
var x = window[a];
if (b) {
x = x[b];
}
document.write(x);
}
And, then you can pass it differently like this:
var def = ["DEF"];
echoVar("def", 0); // def[0]
You could even make this support multiple dimensions if you needed to.
function echoVar(a) {
var x = window[a];
for (var i = 1; i < arguments.length; i++) {
x = x[arguments[i]];
}
document.write(x);
}
var def = {myObject: {length: 3}}
echoVar("def", "myObject", "length"); // def["myObject"]["length"] or def.myObject.length
You can see it work here: http://jsfiddle.net/jfriend00/dANwq/
It would be simpler to lose the brackets and call the item with dot notation
function reval(s, O){
s= String(s);
O= O || window;
var N= s.split(".");
while(O && N.length) O= O[N.shift()];
return O || s;
}
window.def= ['definition'];
alert(reval('def.0'))
/* returned value: (String)
definition
*/
Assume you have an array:
var arrStateCityAll=['CA_Alameda','CA__Pasadena','CA_Sacramento','NY_Albany','NY_Buffalo','NY_Ithaca']
Is there an easy way using javascript and/or jQuery to filter the arrStateCityAll to get a new array (a subset of arrStateCityAll); something like this:
// return's ['CA_Alameda','CA__Pasadena','CA_Sacramento']
var arrStateCityCA=FilterArray('CA',arrStateCityAll);
Likely you want to do a regex on each item. You can do this with jQuery's grep function.
http://api.jquery.com/jQuery.grep/
You can use javascript's Array.filter.
var arrStateCityAll = ['CA_Alameda','CA__Pasadena','CA_Sacramento','NY_Albany','NY_Buffalo','NY_Ithaca']
var arrStateCityCA = arrStateCityAll.filter( function (element) {
return element.indexOf("CA_") == 0;
});
The mozilla documentation linked to above has a solution for browsers that don't implicitly support filter.
This should work.
var arrStateCityCA = [];
for (var i = 0;i<arrStateCityAll.length;i++){
if (arrStateCityAll[i].substr(0,2) == 'CA'){
arrStateCityCA.push(arrStateCityAll[i]);
}
}
You could use jQuery.grep
var arrStateCityCA =
$.grep(arrStateCityAll,function(el,i){return (el.substring(0,2)=='CA')});
Demo at jsfiddle
To implement you actual FilterArray function as shown in your post you could do
function FilterArray(state,arr){
return $.grep(arr,
function(el,i) {return (el.substring(0,2)==state)}
);
}
This makes a few assumptions.
State is always 2 chars.
State is always the first 2 chars.
And of course remember case-sensitivity (this function is case sensitive) ie 'CA' not equal to 'Ca'.
if you are going to have an undescore between your state and city name, you can split on the underscore and test against the first array value
function getSubSetByState(set,state) {
var result = [];
for(var i=0,l=set.length;i<l;++i) {
if(set[i].split('_')[0] === state) {
result.push(set[i]);
}
}
return result;
}
Use if by giving it the set of places, and then the state you are searching for.