Grep and Map
|
|
What is grepping and mapping of an array and how to implement it in JavaScript
|
Grepping is a procedure derived by a language known as Perl, as well as mapping is. - By Grep we mean that given an array (a collection of objects) the procedure executes a repetitive type of operation on each entry once verified that the currently dealt entry meets a certain condition.
- By Map we mean the very same procedure as above, but this time executed on each entry regardless of whatever condition.
These examples provide a method that implements the same kind of application in JavaScript.
The grepMap function family emulates PERL and is conceived to pass as arguments of the functions strings of codes. These functions are relatively simple.
The arrayWalker function (at bottom of page) performs grepping and mapping by emulating the PHP function that has the same name ( array_walker() in PHP), and improves it even a bit probably. Such function is more interesting and relatively more complex. I feature it at bottom for if you're not familiar with grap and map is likely you'd first grasp the concepts with the simplier scripts.
function grepMap(operation,condition){
var output=new Array();
for(var i=0;i<this.length;i++){
if(condition){
output.length++
output[output.length-1]=(eval("this[i]"+condition))? eval("this[i]"+operation): this[i];
}
else{
output.length++
output[output.length-1]=eval("this[i]"+operation)
}
}
return output
}
Array.prototype.grepMap=grepMap
var testArray=new Array("one","two","three","four");
testArray=testArray.grepMap("=2","==two");
alert(testArray);
| Notes |
|
The method is meant to be used as a method linked to a specified object: this is what the Array.prototype.grepMap=grepMap line is for. This means that the method trails the object it manipulates, and links to it with a dot; thus an appropriate invocation might be: anObjectHere.grepMap(operationHere,conditionHere)
|
|
If you provide a condition the method will operate like a Grepping subroutine (that is: it will affect only those elements of an array that met the given condition).
If you do not provide a condition, the method will perform like a Mapping subroutine, which means will execute the operation on every entry in the array.
The main setbacks with this function are:
- It works only on arrays, obviously, so if you invoke it erroneously on an object that won't be an array, it would yield an error message
- You must know the expressions you want to use as conditions and operation and put them within the method like strings (that is, included within quotes).
As you may know some examples of conditions are:
- checking for identity: ==
- checking for lack of identity: !=
- checking for a substring: .indexOf('searched String Here')!=-1 (indexOf returns -1 if no match is found, and the index number of the first letter of the string in the superstring if the segment is found; thus saying !=-1 means different than -1: it has been found a match)
- and so on...
These expressions must be put in between quotes when assigned as an argument to the function, and they will be catched by the function itself and suffixed to each entry of the given array. So for instance if you want to verify if an entry of the given array carries a reference to the word "hallo" you'd write: ".indexOf('hallo')!=-1" Then the expression will be evaluated (that is: compiled, turned into active code) and the presence or absence of the condition verified. If met (that is, the currently parsed entry carries the string "hallo", the operation argument will be applied. To the operation argument apply the same observation as above:
- changing it to something else: = (note: one single =, since it is an assignment not a comparison, whereas the comparison wants two consecutive == signs)
- Adding something to it: +=
- Subtracting something to it (if a number. To check if entries are numbers you'd need to check with another function first): -=
- There is an important setback here: since evaluating will turn the string provided as an argument into executed code, words that may appear in between strings would be stripped of those strings once evaluated with the result that words that are not a keyword in javaScript would be parsed as executable code, then not recognized, and produce an error.
Example, checking if there is the string hello: ".indexOf(hallo)!=-1" In fact, once compiled the word hallo would be turned into executable code, and thus unrecognized since there is no such command in javaScript... To work it around, be sure to put those segments of your condition/operation statements which are meant to be interpreted like strings in between single commas:
".indexOf(hallo)!=-1"
- Eventually, the method is geared to affect the input array: in such a fashion if you needn't to alter the original array but just to produce a new instance of an array whose entries are conveniently changed and tailored upon the provided operation/condition arguments you'd have to produce first a copy of the original array. You can use this snippet (do not use assignments to produce copies, since assignments produces, in javascript, sort of a reference so whatever change on one object would affect the other too!)
function copyArray(array){
var output=new Array();
for(var i=0;i<array.length;i++){
output.length++
output[output.length-1]=array[i]
}
return output}
You'd then initialize your new variable meant to be a copy of the original array like:
var copyArr=copyArray(originalArray)
You can now alter the new array doing:
copyArr.grepMap("+=1","<=9") whereas the code above checks to see whether the currently parsed entry of the array is a number lower than 9 and if so adds 1. Note that since bare numbers which are meant to be interpreted as such are not strings, you must not nest them into single quotes.
Grep and Map 2, 3 and 4
|
|
Another Grep and Map function which allows for executing another function on each entry of an array
|
|
a Jehan Legac photo
a Jehan Legac photo
|
At times may be necessary to grep and map executing an operation which is another function itself and requires each item of the array being passed to it as... an argument!
In all these examples the( let's call it so) X-function that is meant to be passed as an argument (either condition or operation or both) that has to handle each entry as an argument of itself, has to be included as a string as it was also above, and without ending bracket.
Instance: if you wanna check whether entries are or not numbers, you might want to use: isNaN(object here); in such case you will pass the command like:
"isNaN("Note the missing closing bracket.
The major setback is that these versions allow only for methods ending with round bracket. But as far as I know they are the only existing ones in javascript!
- The following variation of grepMap is for those cases where you may need to perform passing each entry of the array as an argument to the condition for the condition argument will hold (be) the X-function
function grepMap2(operation,condition){
var output=new Array();
for(var i=0;i<this.length;i++){
if(condition){
output.length++
output[output.length-1]=(eval(condition+"this[i])"))? eval("this[i]"+operation): this[i];
}
else{
output.length++
output[output.length-1]=eval("this[i]"+operation)
}
}
return output
}
Array.prototype.grepMap2=grepMap2
var testArray=new Array("one","two","three","four");
testArray=testArray.grepMap2("=i+1","isNaN(");
alert(testArray);
The example above checks if each entry is not a number and knowing "i" is the counter of the loop in the function, uses it to swap letters with numbers.
- The following variation of grepMap is for those cases where you may need to perform passing each entry of the array as an argument to the operation argument for the operation argument will hold (be) the X-function
function grepMap3(operation,condition){
var output=new Array();
for(var i=0;i<this.length;i++){
if(condition){
output.length++
output[output.length-1]=(eval("this[i]"+condition))? eval(operation+"this[i])"): this[i];
}
else{
output.length++
output[output.length-1]=eval(operation+"this[i])")
}
}
return output
}
Array.prototype.grepMap3=grepMap3
- The following variation of grepMap is for those cases where you may need to perform passing each entry of the array as an argument to the condition and operation arguments both for both the operation and the condition arguments will hold (be) some X-function , Y-function:
function grepMap4(operation,condition){
var output=new Array();
for(var i=0;i<this.length;i++){
if(condition){
output.length++
output[output.length-1]=(eval(condition+"this[i])"))? eval(operation+"this[i])"): this[i];
}
else{
output.length++
output[output.length-1]=eval(operation+"this[i])")
}
}
return output
}
Array.prototype.grepMap4=grepMap4
Custom functions for grepMap
|
|
You can build functions that can be used from within grepMap
|
The power of the latest formulations is unleashed when you consider that you could pass as a method a function that you previously built and that was shaped to perform complex calculations and to yiled ultimately a boolean (true/false) return. Such as:
testArray=testArray.grepMap("myOperativeFunction(","myConditionCheckMethod(")
Just be sure to omit the closing round bracket when passing your functions as arguments!
Actaully the same goal can be achieved by using grepMap first instance, if you're cunning enough to build functions that can be used as its arguments by using keyword this, such as:
function checkNaN(){
if(isNaN(parseFloat(this))){return true}
return false
}
Object.prototype.checkNaN=checkNaN
Above method, prototyped with keyword Object so to make it able to handle whatever type of data, checks if a given object is or isn't a number and returns true if it is a number, such as:
var x="hallo world"
alert(x.checkNaN()) whereas this latest alert would say: true, since string "hallo world" is not a number: we wonder if it is not a number, thus if it is not a number we return true to mean: hypoesis correct. Of course, you could shape the function differently.
All you have to keep in mind id that grepMap checks for the truth of the condition so your functions must be accomodated accordingly (that is: returning true if the condition upon which the operation has to be performed is met).
In such cases you might use:
var testArray=new Array("one","two","three","four");
testArray=testArray.grepMap("=i+1",".checkNaN()");
alert(testArray);
Finally, do mind that using the first instance of the grepMap functions, if you have a custom method like above you do not have to omit the last bracket but you have to prefix a ... dot!
This because grepMap #1 postfixes to each entry whatever expression passed as condition, thus it needs the dot and does not require to omit the closing bracket for the entry of the array wasn't to be included in within as an argument on its own hand so we didn't have to resort to the trick to omit the closing bracket to allow room to include the object (array entry).
ARRAY WALKER
|
|
The array walker PHP like function with codes and explanations on how to use it
|
A Jehan Legac Picture
 |
This is the section for the arrayWalker function, which takes its name after the PHP array_walker subroutine. First of all your code:
function arrayWalker(array, operation, bluntOp, condition, bluntCond){
//validate:
if(!arrayWalker.arguments.length){return false};
if(arrayWalker.arguments.length==1){return array};
bluntOp=(bluntOp)?1:0;
bluntCond=(bluntCond)?1:0;
operation=(operation && typeof(operation)=="string")?
eval(operation):(operation)?
operation:false;
condition=(condition && typeof(condition)=="string")?
eval(condition):(condition)?
condition:false;
//initialize:
var output=new Array(0);
//run:
if(condition && operation){
for(var i=0;i<array.length;i++){
var conditionMet=false;
if(!bluntCond){
conditionMet=condition(array[i]);
}
else if(bluntCond){
conditionMet=condition();
};
if(!bluntOp){
if(conditionMet){
output[++output.length-1]=operation(array[i]);
continue;
}
output[++output.length-1]=array[i];
}
else if(bluntOp){
if(conditionMet){
operation();
/*don't continue: update output with original entry*/
}
output[++output.length-1]=array[i];
}
}
}
else if(condition && !operation){
for(var i=0;i<array.length;i++){
output[++output.length-1]=(!bluntCond)?
condition(array[i]):condition();
}
}
else{
for(var i=0;i<array.length;i++){
if(!bluntOp){
output[++output.length-1]=operation(array[i]);
}
else if(bluntOp){
operation();
output[++output.length-1]=array[i];
}
}
}
return output;
/*keep this comment to use freely
http://www.unitedscripters.com */}
This arrayWalker function does not require any proptotyping
for it's a static function (a function is said static when it doesn't belong to the pool of properties of an object, say).
Before inspecting its arguments, you must notice two things:
//A Jehan Legac Picture\\

//A Jehan Legac Picture\\ |

//A Jehan Legac Picture\\
|

//A Jehan Legac Picture\\
|
- This time both the operation and condition arguments can't be row code as in previously PERL like examples, but necessarily have to be functions somewhere defined in your script.
- Such functions that will be passed as the condition & operation arguments upon invoking arrayWalker are crafted by you, arguably, and in any case must return something; especially the function meant to be passed as the condition argument must return either true or false. arrayWalker will execute them in its code body upon being invoked and will verify the condition to see if it was met or not antirely assuming and depending on the fact that the condition function would return
false if the condition is not met, or would return true if met.
- arrayWalker adds an entry in an output array each time the operation argument function gets executed: therefore if your operation function is crafted in order to return a value, such returned value will be added to the output array as a new entry.
If you then know that the operation function returns a value (and you certainly know for it's entirely up to you to choose it) and you find that these values are suitable to be saved (as they arguably may be, otherwise there would be no reason to return them), keep in mind these three possibilites with the output array that arrayWalker generates out of the input array:
- To be quite sure the input array gets entirely converted into the output array, set the input array equal to the arrayWalker function:
input=arrayWalker(input, otherArgs)
-
If your operation function does not return values, the output array would probably be a repository of null entries. The fact that arrayWalker produces such array is not detrimental for if you do not assign the arrayWalker output array as the value of some higher scope variable, such newly generated output aray would disappear (garbage collection) as soon as arrayWalker stops executing.
- arrayWalker doesn't affect the original, so the output array can be stored as an independent object. But if the input array entries were something differente than primitive data types, not only the output would be different, but also the corresponding input array entries that weren't primitive data type (on this complex issue a quite clear explanation is my Understanding Pointers)
Now let's inspect a bit deepr the arguments and how they work:
- array argument: must be the array you want to loop.
- operation argument: as we said, a function and can be passed either as the function name between apex (that is: "myFunctionName" with apex and without parenthesis) or like the function name without parenthesis and without quotes as well (such would be a pointer to the function itself, if you've defined it somewhere in your script; this for javaScript uses what I named the stark pointers).
If for some reasons you want to pass only the array and condition arguments, you can pass operation as number zero and this would allow you to loop an array in order to just check if each of its entries meets the condition function test.
- bluntOp argument: defaults to zero, and in such case the operation function would get as its argument the currently looped entry of the input array. If this argument gets passed as 1, the arrayWalker would still execute the operation function, but without passing to it the currently looped array entry. This is an improvement in functionality, for in such way you can execute a function only if certain conditions are met, without this function being necessarily supposed as having to manipulate the currently looped input array entry!
- condition argument: as we already said, must be a function and can be passed either as such function name between apex (that is: "myFunctionName" with apex and without parenthesis) or like the function name without parenthesis and without quotes as well (such would be a pointer to the function itself, if you've defined it somewhere in your script; this for javaScript uses what I named the stark pointers).
- bluntCond argument: likewise the bluntOp argument, but this time the reasoning (quite the same) applies to the condition function and not to the operation function; in other words, if bluntCond is set as 1, the condition function would be executed without passing to it as an argument the currently looped input array entry: this can let you check for some external condition!!
A few possible invocations:
arrayWalker(input, myOPfunc);
arrayWalker(input, myOPfunc,0,myCONDfunc);
arrayWalker(input, "myOPfunc",0,"myCONDfunc");
arrayWalker(input, myOPfunc,1,myCONDfunc);
arrayWalker(input, myOPfunc,0,myCONDfunc,1);
arrayWalker(input, 0,0,myCONDfunc);
Other combinations are probably likely, but these are the only ones I deem relevant.
TOP OF PAGE
|