Bare with my newbness if you can, I am just trying to understand exactly what is going on in this function so I can better tailor it to my use case, and write cleaner/shorter code when needed. The function works of course and I can use it, but it bugs me when I don't fully understand the code I am using. I think I understand for the most part but I am having a hard time putting all of the pieces together. If this is a duplicate then all apologies and please mark it as such but I could not find an exact answer to what I am trying to understand. From other answered questions and outside articles I have been able to understand most of what is going on, but I am still getting stuck on a couple of points.
The exact code is:
const userBy = id => u => u.id == id;
const users = [{ id: 1, name: "Kaio" }, { id: 2, name: "Gabriella" }, { id: 3, name: "Enzo" }];
const id = 2;
const user = users.find(userBy(id));
With my main question centering around:
const userBy = id => u => u.id == id;
I got the code from this article just in case more context is needed, though the aforementioned code should be enough - https://medium.com/#kaiosilveira/mastering-javascript-arrays-beyond-for-and-foreach-loops-e2ecf9dfe3e
I understand that in the 'userBy' function that 'id' is being passed in as the parameter. I also understand that in the shorthand syntax the return statement is implied, and that the find() method has three parameters of it's own (element, index, array), much like a forEach() function which I understand and use often.
To put it all together - I understand that 'id' is being passed in as a parameter, but then if I understand it correctly 'u' is being returned and/or passed in as a parameter of the final function where the specified property 'id' of 'u' is equal to the parameter 'id' that was passed into the original function. And since I know that the find() method is iterating over an array, I can reasonably deduce that 'u' is the current element/index of that array.
The specific part that I am having a hard time understanding is how 'u' is capturing the element/index as it's value. Also why 'u' needs to be in it's own function instead of as another parameter alongside the 'id' parameter of the initiating function.
Like I said, the function works and all is well regardless if I understand it. But I would greatly appreciate anyone who could take the time to explain it to me exactly what is happening. I've researched and understood all that I can on my own, just need a little hand holding on those last few points that I mentioned.
[Post Answered Edit] It was asking me to explain why this question was different from What do multiple arrow functions mean in javascript? . They are very similar, but I think with my specific situation with the added Array.prototype.find method differentiates it enough to warrant it's own explanation. At it's core though I can see how some might label it as the same, so if others feel that way then by all means mark it as a duplicate. Whatever is best for the community is fine by me, I am just grateful that the community took the time to help me understand my question.
First, let's look at what .find is doing. The implementation of .find looks something like this:
Array.prototype.find = function (callback) {
for (let i = 0; i < this.length; i++) {
const match = callback(this[i], i, this);
if (match) {
return this[i];
}
}
}
Find expects you to pass it a function, and that function will be called repeatedly, passing in each element of the array one at a time (plus the index and the array), until one of them passes the test. So a normal use of this would be something like this, to find the user who's id is 2:
users.find((user) => {
if (user.id === 2) {
return true;
}
return false;
});
Or shorter:
users.find(user => user.id === 2);
Now the code you've shown goes one step further. It foresees the possibility of wanting to find user.id === 2, but also user.id === 1, and user.id === 18273, etc. So they've created a helper function named userBy, which can produce other functions. In other words, this code:
const userBy = id => u => u.id == id;
... is a factory for producing functions that look like user => user.id === 2, except it can create functions not just that compare against 2, but whatever id you want.
So with that in hand, a line of code that looks like this:
const user = users.find(userBy(2));
... basically means "create the function user => user.id === 2, and then pass that into users.find"
It's because the userBy function doesn't actually iterate over the array - Array.find takes a function with a Boolean return value, therefore the function doing the actual finding is the anonymous child function u => u.id == id. Here's how it'd look in ES5:
var userBy = function(id) {
return function(u) {
u.id == id;
};
};
Here, the userBy function is returning a callback/testing function, and this is used in Array.find. If you didn't have to pass id as an argument, you could simplify it like so, by only using the inner function:
const userBy = u => u.id == id;
const user = users.find(userBy);
Related
I'm trying to recreate the functionality of the underscore _.invoke for learning purposes and I would like to really understand how it works as it seems to be something not too complicated.
The exercise is asking me to return an array with the result of calling "a" method to it. Ok, so here we start.
_.invoke = function (collection, methodName) {
let result = [];
// debugger;
if (Array.isArray(collection)) { // check if collection is an array.
for (let i = 0; i < collection.length; i++) { // iterate over collection
result.push(Array.prototype.methodName.call(collection[i]));
}
}
console.log('result:', result);
return result;
};
I don't know exactly what method is being past to methodName nor if it has any extra arguments to be forwarded (this I understand it would be used in case I'd use a method that requires args like .reduce for instance if I'm not wrong).
As I understand, when I use the .call method on methodName, it should return (push) the iterated element with the "function" applied onto it. Obviously there is something not right, I have used the debugger to see what it does on each step and once it runs the loop and arrives to the call, it quits the loop and runs to check whatever it is it does in the config file of the test.
I get this message in the error log of the HTML file:
_.invoke(mocks.arr, 'testCall').should.eql(mocks.arr);
_.invoke(mocks.obj, 'testCall').should.eql(mocks.objValuesArr);
argsArr = [mocks.arr, mocks.obj];
_.invoke(mocks.arr, 'testArgs', mocks.arr, mocks.obj);
called.should.be.true;
called = false;
argsArr = [mocks.obj, mocks.arr];
_.invoke(mocks.obj, 'testArgs', mocks.obj, mocks.arr);
called.should.be.true;
The this, thisArg and such are still a little hard for me to understand, can someone explain to me what am I missing here..?
So, after some digging, trial and error, I was totally wrong about my approach to the exercise, so I had to re-make the whole thing.
_.invoke = function (collection, methodName) {
// Spread all arguments into a variable.
let args = [...arguments];
// Since the arguments have been all passed to args, we don't need to call them as we normally would.
// Use an already defined function (_.map) with an iteratee to be passed as method.
return _.map(args[0], function (value) {
// Return the iterated value passed through the function of _.map
// and apply the rest of arguments to the element with the function from _.map if there are any.
return value[args[1]].apply(value, args.slice(2));
});
};
I don't know much about underscore.js, but I'm pretty sure _ isn't defined at all, so maybe do window._.invoke = ... instead to properly define it.
I want to write more shortly. It might be written in one line. How can I do that?
this.xxx = smt.filter(item => item.Id === this.smtStatus.ONE);
this.yyy = smt.filter(item => item.Id === this.smtStatus.TWO);
this.zzz = smt.filter(item => item.Id === this.smtStatus.THREE);
Which array methods should I use?
Thanks for your help!
Well you could shorten it using some destructuring, assuming this is the current scope:
const [xxx, yyy, zzz] = ['ONE', 'TWO', 'THREE'].map(x => smt.filter(item => item.Id === this.smtStatus[x]));
That said, I wouldn't do this, as it's harder to read and maintain.
Steve's answer also gave me another idea, you can use an getter function in your filter function to make your code more compact:
function filterItemListById(list, getter) {
return list.filter(item => item.Id === getter(this.smtStatus));
}
this.xxx = filterItemListById(smt, s => s.ONE);
this.yyy = filterItemListById(smt, s => s.TWO);
this.zzz = filterItemListById(smt, s => s.THREE);
Just because something is short, doesn't mean it is good/better. Code that "reads" should be the priority both for yourself and your teammates.
If you just don't like the look of this, you could always toss the filtering into its own function. Might look like this:
filterItemListById(list, value) {
if (list == null) return null;
return list.filter(item => item.Id === value);
}
this.xxx = filterItemListById(smt, this.smtStatus.ONE);
// etc...
Now, that is assuming you did NOT mean to combine all three lines into one. If that is what you meant, well...
Ok, so you can't assign to multiple variables (this.xxx, this.yyy, this.zzz) like that, usually. I know some people declare multiple variables in one line like:
var myInt1= 0, myInt2= 1, myInt3 = 2; // and so on
Declaring multiple primitives like this is fine, but I would never do this with any complicated logic, too messy looking.
To shorten, you have two options:
Put your logic into one or more functions with descriptive names, nothing wrong with this.
Put your variables into a list and loop over those variables while filtering. This is an awful approach, I like what you already have just fine, nothing wrong with it.
In short, don't worry about writing cute one-liners, you will only confuse yourself and your team later on. Focus on writing readable code so anyone can understand what is going on just by reading it line by line.
Issue Description
I'm currently trying to make updates on passwords using sequelize.fn.
I've seen numerous sites, including the Sequelize's manual, yet I found too few articles that approaches my case, and none of them seems to solve it.
Moreover, the function to encrypt the passwords need nested function, so I need to gradually construct the nested function
by putting the successive arguments into variables.
I have not found anything on constructing the functions, thus using sequelize.fn outside the arguments of the functions find and updates.
Additional context
In my case, " crypt('password', gen_salt('bf', 4)) " (in Postgres) needs to become
sequelize.fn('crypt', 'password', sequelize.fn('gen_salt', 'bf', 4)) and to be stored into a variable
When I inject it into the update function, an Fn Object is injected, and the result in the database seems to be a memory address instead of the result of the crypt function.
I may post a code example so you can have a better understanding of what I'm trying to do and I think should be added into the documentation
I did not made this doc issue to resolve my issue, only to point out what I think is lacking in the documentation
I will ask StackOverflow for help regarding my issue.
//Take a format and a value, and applies the functions inside the format onto the value
function recursSequelizeFn (format, value) {
const sequelize = db.sequelize;
let nextId = 0;
let matches;
let finalArgs = {};
//The RegEx finds the function calls of which parameters aren't functions themselves
//As the format string is modified on each loop, I need to initialize the RegEx on each loop
while ((matches = (/(\w+)\(([^(,)]+)?(?:,([^(,)]+))*\)/g).exec(format)) !== null) {
//matches[0] is the whole expression
//matches[1] is the function's name
//matches[>=2] are the arguments
let func = matches[1];
let args = matches.slice(2);
for (let argNo in args) {
//Transforms DB's columns into Sequelize-interpreted columns
//I do not check the column that is found, because I do not have others columns than the one that is being treated
args[argNo] = args[argNo].replace(/seqCol_(\w+)/g, (correspondance, p1, decalage, chaine)=>{return value;});
//If the parameter was previously saved, we replace its reference by its value
args[argNo] = args[argNo].replace(/(#\d+)/g, (correspondance, p1, decalage, chaine)=>{return finalArgs[p1];});
}
//finally, we save the value of the function and replace the whole expression by a reference
finalArgs['#'+nextId]=sequelize.fn(func, ...args);
format = format.replace(matches[0], '#'+nextId);
nextId++;
}
//When the treatment is finished, we return the last value saved into the array
//Or we return the original value if we have not made any change (the array will be empty)
return finalArgs['#'+(nextId-1)]||value;
}
Object.keys(req.message.attributes).map((key)=>{
//req.message.attributes[key] contains the formatting needed to be put onto the value
//e.g. crypt(password, gen_salt('bf', 4))
//req.body[key] contains the value
//e.g. '1234'
let seqValue = util.recursSequelizeFn(req.message.attributes[key], req.body[key]);
req.body[key] = seqValue;
});
//mainModel is a sequelize model
mainModel.update(req.body, {where: {id:req.params.id}, logging:console.log})
.then() //...
I know I'm going to post this and then get it working, not to mention the title but...
I'm in a situation where I need to do some async code to each element in an array that I'm given, and then do some checks after all of them have finished. Normally, if I would use async.js here, however the way I'm doing it isn't getting my the results I want.
So this is my logic, I figure I can create an array of functions, however the way I'm doing this seems to have async.js returning me a list of functions in the result.
findLeadThatCanBeTaken : (leads, user, manualTake, cb) =>
if(leads.length == 0) then return cb(null, null)
funcArr = []
self = this
for lead in leads
fun = (callback) ->
console.log(lead.state)
State.get lead.state, (e, state) ->
if !state or state.canBeCalled(self.currentTime())
return callback(null, lead._id.toString)
return callback(null, null)
funcArr.push(fun)
async.parallel funcArr, (e, r) ->
if (e)
return cb(message:'No leads can be called at the current time', null)
id = _.compact(r)
id = r[0] if r.length
if (!id || !id.length)
return cb(message:'No leads can be called at the current time', null)
lead = _.filter leads, (l) -> return l._id.toString() == id
# Code handling
#takeLead(lead, user, cb)
I'm 90% sure the issue is this array I am creating isn't being assigned like I think it is, but I'm not sure. Anyone know what I'm doing wrong here?
I'd guess that you have a classic "closure inside a loop" problem:
for lead in leads
fun = (callback) ->
# some stuff that uses 'lead'...
The problem is that all your fun functions will have a reference to exactly the same lead and when those functions execute, lead will refer to the last value it had in the loop.
From the fine manual (bottom of that section):
When using a JavaScript loop to generate functions, it's common to insert a closure wrapper in order to ensure that loop variables are closed over, and all the generated functions don't just share the final values. CoffeeScript provides the do keyword, which immediately invokes a passed function, forwarding any arguments.
Sound familiar? You probably want to get the value of lead on each iteration rather than just dragging around lead itself with something like this:
for lead in leads
do (lead) ->
fun = (callback) ->
# What you have now...
Scenario: I'm searching for a specific object in a deep object. I'm using a recursive function that goes through the children and asks them if I'm searching for them or if I'm searching for their children or grandchildren and so on. When found, the found obj will be returned, else false. Basically this:
obj.find = function (match_id) {
if (this.id == match_id) return this;
for (var i = 0; i < this.length; i++) {
var result = this[i].find(match_id);
if (result !== false) return result;
};
return false;
}
i'm wondering, is there something simpler than this?:
var result = this[i].find(match_id);
if (result) return result;
It annoys me to store the result in a variable (on each level!), i just want to check if it's not false and return the result. I also considered the following, but dislike it even more for obvious reasons.
if (this[i].find(match_id)) return this[i].find(match_id);
Btw I'm also wondering, is this approach even "recursive"? it isn't really calling itself that much...
Thank you very much.
[edit]
There is another possibility by using another function check_find (which just returns only true if found) in the if statement. In some really complicated cases (e.g. where you don't just find the object, but also alter it) this might be the best approach. Or am I wrong? D:
Although the solution you have is probably "best" as far as search algorithms go, and I wouldn't necessarily suggest changing it (or I would change it to use a map instead of an algorithm), the question is interesting to me, especially relating to the functional properties of the JavaScript language, and I would like to provide some thoughts.
Method 1
The following should work without having to explicitly declare variables within a function, although they are used as function arguments instead. It's also quite succinct, although a little terse.
var map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return this.id == match_id ? this : map(this, function(u) {
return find.call(u, match_id);
}).filter(function(u) { return u; })[0];
};
How it works:
We test to see if this.id == match_id, if so, return this.
We use map (via Array.prototype.map) to convert this to an array of "found items", which are found using the recursive call to the find method. (Supposedly, one of these recursive calls will return our answer. The ones which don't result in an answer will return undefined.)
We filter the "found items" array so that any undefined results in the array are removed.
We return the first item in the array, and call it quits.
If there is no first item in the array, undefined will be returned.
Method 2
Another attempt to solve this problem could look like this:
var concat = Function.prototype.call.bind(Array.prototype.concat),
map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return (function buildObjArray(o) {
return concat([ o ], map(o, buildObjArray));
})(this).filter(function(u) { return u.id == match_id })[0];
};
How it works:
buildObjArray builds a single, big, 1-dimensional array containing obj and all of obj's children.
Then we filter based on the criteria that an object in the array must have an id of match_id.
We return the first match.
Both Method 1 and Method 2, while interesting, have the performance disadvantage that they will continue to search even after they've found a matching id. They don't realize they have what they need until the end of the search, and this is not very efficient.
Method 3
It is certainly possible to improve the efficiency, and now I think this one really gets close to what you were interested in.
var forEach = Function.prototype.call.bind(Array.prototype.forEach);
obj.find = function(match_id) {
try {
(function find(obj) {
if(obj.id == match_id) throw this;
forEach(obj, find);
})(obj);
} catch(found) {
return found;
}
};
How it works:
We wrap the whole find function in a try/catch block so that once an item is found, we can throw and stop execution.
We create an internal find function (IIFE) inside the try which we reference to make recursive calls.
If this.id == match_id, we throw this, stopping our search algorithm.
If it doesn't match, we recursively call find on each child.
If it did match, the throw is caught by our catch block, and the found object is returned.
Since this algorithm is able to stop execution once the object is found, it would be close in performance to yours, although it still has the overhead of the try/catch block (which on old browsers can be expensive) and forEach is slower than a typical for loop. Still these are very small performance losses.
Method 4
Finally, although this method does not fit the confines of your request, it is much, much better performance if possible in your application, and something to think about. We rely on a map of ids which maps to objects. It would look something like this:
// Declare a map object.
var map = { };
// ...
// Whenever you add a child to an object...
obj[0] = new MyObject();
// .. also store it in the map.
map[obj[0].id] = obj[0];
// ...
// Whenever you want to find the object with a specific id, refer to the map:
console.log(map[match_id]); // <- This is the "found" object.
This way, no find method is needed at all!
The performance gains in your application by using this method will be HUGE. Please seriously consider it, if at all possible.
However, be careful to remove the object from the map whenever you will no longer be referencing that object.
delete map[obj.id];
This is necessary to prevent memory leaks.
No there is no other clear way, storing the result in a variable isn't that much trouble, actually this is what variables are used for.
Yes, that approach is recursive:
you have the base case if (this.id==match_id) return this
you have the recursive step which call itself obj.find(match_id) { ... var result = this[i].find(match_id); }
I don't see any reason, why storing the variable would be bad. It's not a copy, but a reference, so it's efficient. Plus the temporary variable is the only way, that I can see right now (I may be wrong, though).
With that in mind, I don't think, that a method check_find would make very much sense (it's most probably basically the same implementation), so if you really need this check_find method, I'd implement it as
return this.find(match_id) !== false;
Whether the method is recursive is hard to say.
Basically, I'd say yes, as the implementations of 'find' are all the same for every object, so it's pretty much the same as
function find(obj, match_id) {
if (obj.id == match_id) return obj;
for (var i = 0; i < obj.length; ++i) {
var result = find(obj[i], match_id);
if (result !== false) return result;
}
}
which is definitely recursive (the function calls itself).
However, if you'd do
onesingleobjectinmydeepobject.find = function(x) { return this; }
I'm not quite sure, if you still would call this recursive.