MongoDB javascript pass argument - javascript

i have a question about passing an arugment in MongoDB javascript to a map function.
Currently, what i had in mind is something like this:
var map = function(n) {
if(this.x == n){
emit(this.x);
}
}
var reduce = function(key, values) {
values.forEach(function(x) {
//do something
});
return {nd:values};
}
db.smsdb.mapReduce(map(2), reduce, "collection")
But as i have tried to do this, the shell returns an error "not code"...so i'm guessing i'm not doing this the right way.
Does anyone have the right solution for this kind of problem, i would be more than glad to get it right.
Thanks

This line:
db.smsdb.mapReduce(map(2), reduce, "collection")
is calling map(2) with the result (undefined) being passed as the map function for mapReduce.
Instead do something like this:
db.smsdb.mapReduce(function(){ map.call(this, 2); }, reduce, "collection")
UPDATE
The above doesn't work because the map function isn't available in the scope the mapReduce map function is run. So you have to wrap it up into a single function that can generate the map function you need:
var mapper = function(n) {
function map() {
if(this.x == n){
emit(this.x);
}
}
return map;
};
db.smsdb.mapReduce(mapper(2), reduce, "collection");

scope In Mapreduce Stands For Global Parameters In Map Reduce
As pointed out by #Dave Griffith, you can use the scope parameter of the mapReduce function.
I struggled a bit to figure out how to properly pass it to the function because, as pointed out by others, the documentation is not very detailed. Finally, I realised that mapReduce is expecting 3 parameters:
map function
reduce function
object with one or more of the params defined in the doc
Eventually, I arrived at the following code in Javascript:
// I define a variable external to my map and to my reduce functions
var KEYS = {STATS: "stats"};
function m() {
// I use my global variable inside the map function
emit(KEYS.STATS, 1);
}
function r(key, values) {
// I use a helper function
return sumValues(values);
}
// Helper function in the global scope
function sumValues(values) {
var result = 0;
values.forEach(function(value) {
result += value;
});
return value;
}
db.something.mapReduce(
m,
r,
{
out: {inline: 1},
// I use the scope param to pass in my variables and functions
scope: {
KEYS: 1,
sumValues: 22// of course, you can pass function objects too
}
}
);

You can do this:
db.smsdb.mapReduce(function() { return map(2); }, reduce, "collection")

I should also point out that this solution also works:
var map = function() {
if(this.x == n){
emit(this.x);
}
}
db.smsdb.mapReduce(mapper, reduce, {out:"nodes",scope:{n:2}});

Related

How to Define Map in Terms of ForEach

I have defined my own map function, which serves the same purpose as the method on Array objects. (I am doing this to learn). The code is below.
I would like to use Array's forEach method inside of my own definition of map, but I am missing something conceptually. How would I be able to do this using forEach?
Working without using forEach
// transforming with map
// NOTE: map is already defined in JavaScript
function map(array, transformation) {
var mapped = [];
for (var index in array) {
var currentElement = array[index];
var transformedElement = transformation(currentElement);
mapped.push(transformedElement);
}
return mapped;
}
My Attempt of using forEach
function mapForEach(array, transformation) {
var mapped = [];
array.forEach(transformation(currentVal, index, array, mapped));
return mapped;
}
function transformation(person, index, array, accumulator) {
var name = person.name;
accumulator.push(name);
}
The missing concept here is pass by value. The changes to accumulator will not be reflected in forEach. forEach is not actually designed to return a value for each iteration. For that you have map.
Taken from here,
foreach iterates over a list and applies some operation with side
effects to each list member (such as saving each one to the database
for example)
map iterates over a list, transforms each member of that list, and
returns another list of the same size with the transformed members
(such as converting a list of strings to uppercase)
Try this code:
function mapForEach(array, transformation) {
var mapped = array.map(transformation);
return mapped;
}
function transformation(person, index, array) {
var name = person.name;
return name;
}
Here is a JSFiddle
If you absolutely have to use forEach, then rather than passing a value, a global variable has to be used. This would be then
var accumulator =[];
function mapForEach(array, transformation) {
array.forEach(transformation);
return accumulator;
}
function transformation(person, index, array) {
var name = person.name;
accumulator.push(name);
}
JSFiddle - forEach
You're almost right with your method, but like #Katana314 says, you need to bind, not call.
Here's how I would do it:
function map(array,mapper) {
var ret=[];
array.forEach((val,key)=>{
//Since arrays are supposed to be sequential, we don't have to worry
//about keys matching, if they don't match, the user/developer did
//something horribly wrong.
ret.push(mapper(val,key,array));
});
return ret;
}
And here's how I would fix your way.
function mapForEach(array, func) {
var mapped = [];
array.forEach(mapForEachPusher.bind(null,mapped,func));
return mapped;
}
function mapForEachPusher(mapped,func,value,index,array) {
mapped.push(func(value,index,array));
}
function extract_name(person) {
return person.name;
}
mapForEach(
[{name:'Billy'},{name:'Bob'},{name:'Bridget'},{name:'Brittany'},{name:'"B word"'}]
,extract_name
);
//["Billy", "Bob", "Bridget", "Brittany", "\"B word\""]
It's effectively the same thing, your way moves the .push() call out of the main function, which means you have to pass data around, whereas I only need to reference from the outer scope. The only downside to my way is the overhead of the function, but it is an arrow function, so that might make it lightweight enough to not matter.

Javascript, syntax after function

I have a function that changes an object for me and I'm wondering how a bit of it works and would be grateful if someone could explain it or point me in the right direction. Here is the function:
$scope.filteredObject = Object.keys($scope.filterObject).reduce(function(p, collection) {
var values = $scope.filterObject[collection];
p[collection] = Object.keys(values).filter(function(key) {
return values[key] === true;
}).map(function(key) {
return key;
});
return p;
}, {});
So this works great, but I'm wondering what the }, {}); at the end of the function does exactly. Im not exactly sure the name of that and googleing "}, {} after a function in javascript" seems to confuse the hell out of google (lol). Thanks!
} - end of the anoymous function expression function(p, collection) { … }
, - delimiter between multiple arguments
{} - (empty) object literal, the second argument
) - end of function invocation, closing parentheses for the arguments list to .reduce(…)
It is an empty object and has nothing to do with the function.
Have a look at the Array.prototype.reduce()
The reduce function has a second optional parameter.
arr.reduce(callback[, initialValue])
So in your case it's like this:
callback = function(p, collection) { /*...*/ };
initialValue = {}; // could also be new Object()
Hope this helps :)

how to add an argument to a method stored in an array that is called later

This is a follow-up to this question (although this is self-contained) trying to `call` three methods but not working correctly with jQuery map.
I am trying to store a set of methods in an array but there is a set that might have arguments like below (the initial methods are in before_methods and the proposed methods are in lm_methods). I'm sure it's pretty self explanatory what I want but I'd like to be able to merge in the arguments into a reasonable call to f (specifically the arc.pLikedByTerm). I currently have the following:
// signature
pLikedByTerm:function(term, ne, sw, m){
....
}
// code before_methods just to show
this.before_methods=[arc.pLocations,arc.pLikedLocations,arc.pLikedItems];
this.lm_methods=[arc.pLocations,arc.pLikedLocations,arc.pLikedItems, arc.pLikedByTerm('surfing'),arc.pLikedByTerm('sailing')];
$.each(this.lm_methods, function(i,f){
f(ne,sw,m);
});
How would I do this or is this bad design? What would be the idiomatic way? My brain is fried.
thx in advance
Update 1
Playing around with answer below, it looks like this works which might the simplest things:
var fns=[logStuff("this is msg"), logMoreArgs("a term","a you msg")];
for (var i=0; i<fns.length; i++) {
fns[i];
}
Having an array of functions is common practice when used often. For example, consider this Callback class.
function Callback(){
this.callbacks = [];
}
Callback.prototype.run = function(cb) {
for (var i=0; i<this.callbacks.length; i++) {
this.callbacks[i]();
}
};
We can then add some callbacks.
function logStuff(msg) {
jsprint(msg || "No message");
}
obj = new Callback();
obj.callbacks.push(logStuff);
obj.callbacks.push(logStuff);
obj.run();
If we run this we see that it's only logging our default value. So if we want to bind some data, we can use the bind function.
Function.prototype.bind
thisArg
The value to be passed as the this parameter to the target
function when the bound function is called. The value is ignored if
the bound function is constructed using the new operator.
arg1, arg2, ...
Arguments to prepend to arguments provided to the bound function
when invoking the target function.
Our new code sets the first parameter to different strings, which we then see. You can bind any number of parameters.
obj = new Callback();
obj.callbacks.push(logStuff.bind(null, "My message"));
obj.callbacks.push(logStuff.bind(null, "My other message"));
obj.run();
end result
The way you are doing would work just ok. Just remove the arguments and parens:
Instead of:
this.lm_methods=[arc.pLocations,arc.pLikedLocations,arc.pLikedItems,
arc.pLikedByTerm('surfing'),arc.pLikedByTerm('sailing')];
Do:
this.lm_methods=[arc.pLocations,arc.pLikedLocations,arc.pLikedItems,
arc.pLikedByTerm,arc.pLikedByTerm];
Example:
function say(txt) {
console.log("say" + txt);
}
function shout(txt) {
console.log("shout" + txt);
}
function whisper(txt) {
console.log("whisper" + txt);
}
var funcArr = [say, shout, whisper];
$.each(funcArr, function(i, f) {
f("hello");
});
would print:
sayhello
shouthello
whisperhello

How can rewrite function instead of reference?

var BigObject = (function() {
function deepCalculate(a, b, c) {
return a + b + c;
}
function calculate(x) {
deepCalculate(x, x, x);
}
return {
calculate: calculate,
api: {
deepCalculate: deepCalculate
}
}
})();
This is basic self executing function with private function I keep in api.
The problem I have is that now I can't overwrite deepCalculate from the outside of the function.
How is that a problem? I use Jasmine and want to test if function was called. For example:
spyOn(BigObject, 'calculate').andCallThrough();
expect(BigObject.api.deepCalculate).toHaveBeenCalled();
fails. However as I debug, I am sure that Jasmine binds BigObject.api.deepCalculate as a spy, however from the inside calculate still calls original deepCalculate function and not the spy.
I would like to know how can I overwrite the function and not just a reference for it.
The simple answer would be:
(function ()
{
var overWriteMe = function(foo)
{
return foo++;
},
overWrite = function(newFunc)
{
for (var p io returnVal)
{
if (returnVal[p] === overWriteMe)
{//update references
returnVal[p] = newFunc;
break;
}
}
overWriteMe = newFunc;//overwrite closure reference
},
returnVal = {
overWrite: overWrite,
myFunc: overWriteMe
};
}());
Though I must say that, I'd seriously think about alternative ways to acchieve whatever it is you're trying to do. A closure, IMO, should be treated as a whole. Replacing parts of it willy-nilly will soon prove to be a nightmare: you don't know what the closure function will be at any given point in time, where it was changed, what the previous state was, and why it was changed.
A temporary sollution might just be this:
var foo = (function()
{
var calc = function(x, callback)
{
callback = callback || defaultCall;
return callback.apply(this, [x]);
},
defaultCall(a)
{
return a*a+1;
},
return {calc: calc};
}());
foo(2);//returns 5
foo(2,function(x){ return --x;});//returns 1
foo(2);//returns 5 again
IMO, this is a lot safer, as it allows you to choose a different "internal" function to be used once, without changing the core behaviour of the code.

How to shift "arguments"?

Here's the script:
function runScripts() {
if (arguments.length === 0) return;
chrome.tabs.executeScript(null, {
file: arguments[0]
}, function() {
arguments.shift();
runScripts.apply(null, arguments);
});
}
It doesn't work because arguments is not actually an array, it's just array-like. So how can I "shift" it or hack off the first element so that I can apply this function recursively?
var params = Array.prototype.slice.call(arguments);
params.shift();
You can check out this blog post which explains it in further detail.
I assume you want to reference the original arguments, instead of that from the callback you're passing to chrome.tabs.executeScript.
If so, you'll need to cache it first.
function runScripts() {
if (arguments.length === 0) return;
var args = [];
Array.prototype.push.apply( args, arguments );
chrome.tabs.executeScript(null, {
file: args.shift();
}, function() {
// using the modified Array based on the original arguments object
runScripts.apply(null, args);
});
}
[].shift.call(arguments) is also valid. I'm using this in production code and it works as expected.
With this approach, your function becomes a bit more succinct:
function executeScripts() {
if (arguments.length === 0) return;
chrome.tabs.executeScript(null, {
file: [].shift.call(arguments)
}, function() {
executeScripts.apply(null, arguments);
});
}
If you look on MDN, they state that shift() was implemented with this flexibility in mind.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/shift
You can transform arguments into a regular array like this:
var args = Array.prototype.slice.call(arguments);
Just wanted to point out a potential problem with [].shift.call(arguments).
This seems to have the perhaps unclear intent of shifting your arguments - even for your function's named parameters - even if used prior to the shift statement.
For example,
function testShift (param1, param2) {
[].shift.call(arguments);
if (param1=="ONE") alert("ONE");
}
If you make the following call, what might you expect to happen?
testShift("ONE", "TWO");
If you expected param1 to stay "ONE", your fix is to set a var to param1 before the shift occurs. It looks like javascript is not binding param1 until the line it is called on - not when the function is called... so modifications to arguments prior to a parameter being used can have unexpected effects.
Hopefully now, you'll be able to expect it.
In ES6 you can now use Array.from() MDN ref
e.g.
const args = Array.from(arguments);
const str = args.shift();
You'll need to convert it to an array and then shift. Or, alternatively, drop the first item when converting to an array. Array.prototype.slice.call(arguments, 1) would work for this.
In newer versions of JS, we can now write:
function f(first, ...rest) { ... }
Or
function f() {
const [first, ...rest] = arguments;
}
Which is a little nicer than "shifting" off the first arg. However, if we did want to, we could first convert arguments into a proper array via Array.from or [...arguments].
Here is an article explains this really well. I copied some key points below.
http://www.javascriptkit.com/javatutors/arrayprototypeslice.shtml
For array, remember you can call the slice function to get a sub array.
var abc = [1,2,3,4,5];
abc.slice(0); //[1,2,3,4,5]
abc.slice(1,3); //[2,3]
Since the argument object is only array like, not really an array. The call() / apply() function basically just "borrow" the slice function from Array and use it on the Argument object, and you can even pass parameters into the slice function just as acting on the array.
var myobject ={ // array-like collection
length: 4,
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three'
}
var myarray = Array.prototype.slice.call(myobject)
// returns myobject as a true array: ["zero", "one", "two", "three"]
var myarray = Array.prototype.slice.call(myobject, 1)
// returns ["one", "two", "three"]
The one remaining question is why we're calling slice() on the prototype object of Array instead of an array instance. The reason is because this is the most direct route to accessing the slice() method of Array when that's all we're interested in; we could have first created an array instance, but that's less efficient and arguably more abstruse:
var myarray = new Array().prototype.slice.call(myobject) // less efficient
You could convert the arguments to an actual array and then use that array in the rest of your logic in the function.
function runScripts()
{
var i=0, l=arguments.length, arr=[];
while(i<l)
{
arr.push(arguments[i++]);
}
...rest of your function code
Edit to add: i've had issues with prototype and call in older versions of IE, so it really depends on what support you'll need.
I went with this:
function executeScripts() {
if (arguments.length === 0) return;
var args = Array.prototype.slice.call(arguments);
chrome.tabs.executeScript(null, {
file: args.shift()
}, function() {
executeScripts.apply(null, args);
});
}
It's useful when writing Google Chrome Extensions. I wanted to use jQuery in my content script, but then you have to load it first. Turns out out by chaining calls to chrome.tabs.executeScript you can do this:
chrome.browserAction.onClicked.addListener(function(tab) {
executeScripts('jquery-1.4.4.min.js', 'content.js');
});

Categories

Resources