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.
Related
I am curious about the performance difference between initializing a function outside a loop vs inline:
Outside the loop:
const reducer = (acc, val) => {
// work
};
largeArray.reduce(reducer);
Inline:
largeArray.reduce((acc, val) => {
// work
});
I encounter this kind of situation regularly, and, unless I'm going to reuse the function, it seems useful to avoid introducing another variable into my scope by using the inline version.
Is there a performance difference in these two examples or does the JS engine optimize them identically?
For example: is the inline function being created every time the loop runs and then garbage collected? And if so:
What kind of effect does this have on performance, and
Does the size of the function affect this? For example, a function that is 200 vs 30_000 unicode characters.
Are there any other differences or things I'm not considering?
Hopefully you understand my train of thought and can provide some insight about this. I realize that I can read all of the docs and source code for V8 or other engines, and I would get my answer, but that seems like an overwhelming task to understand this concept.
I did run test on jsben
SET1(random used twice): http://jsben.ch/8Dukx
SET2:(used once): http://jsben.ch/SnvxV
Setup
const arr = [ ...Array(100).keys() ];
const reducer = (acc, cur) => (acc + cur);
Test 1
let sumInline = arr.reduce((acc, cur) => (acc + cur), 0);
let sumInlineHalf = arr.slice(0, 50).reduce((acc, cur) => (acc + cur), 0);
console.log(sumInline, sumInlineHalf);
Test 2
let sumOutline = arr.reduce(reducer, 0);
let sumOutlineHalf = arr.slice(0, 50).reduce(reducer, 0);
console.log(sumOutline, sumOutlineHalf);
Be surprised
What kind of effect does this have on performance, and
None.
Does the size of the function affect this? For example, a function that is 200 vs 30_000 unicode characters.
Functions aren't executed as "unicode characters". It doesn't matter how "long" the code is.
Are there any other differences or things I'm not considering?
A very important one: Code is written for humans, not computers. And why do you even ask me?
is the inline function being created every time the loop runs and then garbage collected?
That would be unneccessary and slow. So probably: no.
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);
I'm learning functional programming and I'm trying to refactor a piece of old code using lodash FP.
Here's the code I have:
_.filter(x => isIdInArray(x)(xs))(otherXs)
It's too complicated to read and makes me feel a bit weird (smell ?)
My problem is that x value, that is the first argument of isIdInArray is declared this way:
const getId = _.get('id');
const isIdInArray = _.compose(_.includes, getId);
I can't use my lodash filter function this way:
_.filter(isIdInArray(xs))(otherXs)
I don't even know if it's feasible, but I'm pretty sure I can do something clearer or more readable.
Do you have some ideas ?
Try not to stuff all the fancy features that lodash gives you into a single line there.
Having a complex mechanism in one line may seem nice, but if you can't read it anymore its not very helpful at all.
For managing collections i usually use approaches like this:
var collection = [{id: 'a', someField: 1}, {id:'b', someField: 2}];
var theIdsYoureLookingFor = ['a'];
var filtered = collection
.filter(anyObject => _.includes(theIdsYoureLookingFor, anyObject.id))
.map(anyObject => anyObject.someField);
alert(filtered); // alerts 1
Which parses a collection of objects, filters for those who have an id that you consider valid and then maps those objects to a certain field.
Also don't ever use variable names like: x, xs
If you're writing production code, I recommend using a higher-level function. In your particular case I'd say you need _.intersectionBy:
const keepIfIdInArray = _.intersectionBy('id'); // 'id' can be replaced by your getId
const keepIfIdInOtherXs = keepIfIdInArray(otherXs);
keepIfIdInOtherXs(xs);
If you're doing this as an exercice, then I'd say you may need to decompose a little more. Notice that in lodash/fp, _.includes is curried so you should be able to write the following:
const getId = _.get('id');
const isIdInArray = arr => _.compose(_.includes(arr), getId);
const isIdInOtherXs = isIdInArray(otherXs);
_.filter(isIdInOtherXs)(xs);
I decided to code a simple todo app using Ramda, but I have been stuck with one refactoring related issue. Here're two functions that I think could be refactored:
const isItemCompleted = R.pipe(
R.prop("states"),
R.contains("completed")
)
const isItemEdited = R.pipe(
R.prop("states"),
R.contains("editing")
);
As you can see, there is some code duplication and this would get even messier if I had more states. I have been trying to isolate the duplicated functionality as such:
const statesContains = R.flip(R.pipe(
R.prop('states'),
R.contains()
))
//I would like to use it like this:
const isItemCompleted = statesContains("completed")
const isItemEdited = statesContains("editing")
But I just cannot wrap my head around this. I can make it work with different argument ordering, but I would like to follow the data-last rule to create concise functions.
The data being passed to these isItemCompleted and isItemEdited functions could be something like this:
let item = {states:["editing", "complete"]};
isItemCompleted(item); //true
Any (functional) ideas?
There are several ways to go with this.
Perhaps the most straightforward is
const statesContains = R.curry(
(state, item) => R.contains(state, R.prop('states', item))
);
const isItemCompleted = statesContains("completed");
But it's reasonable to want to abstract this a bit, to allow the property to be searched to vary as well. So you could write:
const propContains = R.curry(
(propName, state, item) => R.contains(state, R.prop(propName, item))
);
const editorsContains = propContains('editors')
const edFred = editorsContains('fred');
// or edFred = propContains('editors', 'fred');
Both of these are reasonable. But Ramda has a function which reads really well, and will serve these needs pretty well, where. With this, you can simply write:
const isItemCompleted = R.where({states: R.contains('completed')});
This, I believe, is the simplest approach if you're looking for one-offs. But both of the above could help you create reusable functions.
You can see all this in action in the Ramda REPL
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.