Cleanest way to use multiple values in a complex routine? - javascript

See the following pseudocode snippet that approximates my situation:
function foo () {
for ( velocity=0; velocity<100; velocity++ ) {
root1 = computeRoot1();
root2 = computeRoot2();
// do a bunch of computation with root1
// if result of computation is undesirable, do computations again with root2
}
So, basically I want to do the computations in the body of the for loop with root1, and then root2 if root1's computation result is invalid.
My first instinct was the obvious approach, to wrap the computation in a help function, but I'm not sure this is the most clear approach. I'm trying for good collocation of information in my code, and a function call for code that will be executed at most twice (per iteration) defeats that goal without providing a great deal of conciseness to my code.
I was thinking perhaps a for loop like:
for ( root=root1; root1IsInvalid==true || bothRootsInvalid==true; root=root2 )
or a while with similar functionality. But I'm certainly open to other suggestions
As someone reading this code, which approach would make it the most readable and concise to you?
As an aside, I'm writing this particular function in JavaScript, but language-agnostic solutions would be awesome.
EDIT: clarified code snippet

You have several basic approaches:
Put the values in an array and use a for loop to run the same code on each item in the array, perhaps stopping the iterations when some condition is met.
Create a function that does the computation and then just write code that calls the function on the first one, then the second one and so on.
Create a while loop and repeat your code until some condition is met.
The first option is easier to extend to N items. The second option is perhaps simpler for just two items.
You can make the computation function be a local function (declared and used inside the function you are currently executing) so it doesn't add to the global namespace and your code remains more encapsulated.
I'm also not sure what you intend to be doing with this line:
root1, root2 = computeRoots();
But, it is only assigning the value to root2 and it looks like you probably want var in front of these to define them as local variables.

If eager evaluation is OK, you can collect your roots into an array and use roots.filter(isinvalid) to take out the invalid ones; then just use the first item in the resulting array.
If you need lazy evaluation, you can generalize this into a function that lazily evaluates a function over an array until a non-null result is found:
// call fn on items in arr until fn returns non-null
// returns [item, result]
// if result===false, no true value was returned
function firstNotNull(fn, arr) {
var i, length, item, result=null;
for (i = 0, length=arr.length; i < length; i++) {
item = arr[i];
result = fn(item);
if (result!==null) {
break;
}
}
return [item, result];
}
function rootComputations(root) {
var computationResult = null;
if (root==1) {
computationResult = 1;
}
return computationResult;
}
function computeRoots() {
return [0,1];
}
function foo() {
var velocity, roots, root, result, computations;
for (velocity = 0; velocity < 100; velocity++) {
roots = computeRoots();
computations = firstNotNull(rootComputations, roots);
console.log(computations);
root = computations[0];
result = computations[1];
}
}
foo();
You can generalize firstNotNull() even further:
// call fn on items in arr until cond(fn(item)) returns true
// returns [item, fn(item)], or null if unsatisfied
function firstSatisfying(cond, fn, arr) {
var i, length, item, fnitem, result=null;
for (i = 0, length=arr.length; i < length; i++) {
item = arr[i];
fnitem = fn(item);
if (cond(fnitem)) {
result = [item, fnitem];
break;
}
}
return result;
}
var firstNotNull = firstSatisfying.bind(null, function(item){return item!==null;});
You now have a generic function for taking the first of a list of things that satisfies any condition you want.
ECMAScript 5 added many methods which make eager functional application over arrays much easier, but Javascript doesn't have any native facilities for lazy evaluation. If this is something you think you'll be needing often, consider using stream.js, which provides a "stream" datatype with methods for partial application. Using stream.js, your logic would look like this:
// rootStream should be a function which returns a Stream
// It should construct a stream with the first root produced
// and a function that returns the remaining roots.
// Since I don't know how you get your roots, I'll use a stupid example:
function rootStream() {
return new Stream(0, function(){
return new Stream(1);
});
}
function isvalid(root) {
return root===1;
}
Stream.range(0,100)
.walk(function(v){
//v doesn't seem to be used?
var firstvalid = rootStream().filter(isvalid).head();
console.log(firstvalid);
});

Related

can / should I replace this for loop with a `.forEach` (or `.find`)

I have something like this:
function myRatherLongFunction(){
... some computation ...
var items = ... some array ...
if (some condition) return something
... more computations ...
if (some condition) return something
... more computations ...
for (var i=0; i<items.length; i++){
var item = items[i];
var blah1 = ...something...
var blah2 = ...something...
var blah3 = ...something...
if (loremIpsum(i,item,blah1,blah2,blah3)) {
return someFunction(i,item,blah1,blah2,blah3);
}
}
... even more computations ...
}
Though I think (or feel) that .forEach is somewhat 'overhyped', I do like to use it, since it often saves typing, among other things. So, in above situation, how would I do that (talking about the for loop) with .forEach?
You see, if the items array has, say, 10 million entries, and I find something right at the beginning, I sure do not want to continue running a pointless search and make my code slow.
I know about Array.find and Array.findIndex, but they just return item or i, respectively.
I know I could throw an error (with message "item found") inside the .forEach callback function when the item is found, and then catch an error (with that same message) just outside the .forEach, but that all would be more typing than the straight for loop, so I do not want to do it that way. Any way this can be done with less typing (than straight for loop), not more?
Using .find or .findIndex, storing blah1 etc somehow, and retrieving the stored values after .find returns (so someFunction can be called thereafter) can certainly be made to work, but I doubt very much that that would be less typing compared to just keeping good old retro style.
UPDATE
Please consider Bergi's comment to be the accepted answer.
For me, this code is easier to understand (amount of typing is almost the same):
function findShoeSizeOfFirstItemSuchThat(items) {
let blah1, blah2, blah3;
let index = items.findIndex((item, index) => {
blah1 = ...something...
blah2 = ...something...
blah3 = ...something...
return loremIpsum(index, item, blah1, blah2, blah3)
});
if (index != -1) return someFunction(index, items[index], blah1, blah2, blah3);
}

Jquery test suite does not pass

I'm the beginner in the jquery and functional programming
I transformed the jquery function into the functional programming paradigm and the following one is the original jquery function
merge: function( first, second ) {
var len = +second.length,
j = 0,
i = first.length;
for ( ; j < len; j++ ) {
first[ i++ ] = second[ j ];
}
first.length = i;
return first;
},
And I transformed that like
merge: function( first, second ) {
var len = +second.length,
j = 0,
i = first.length;
let main = (first,second)=>
{return R.concat(first, second);}
first.length = i;
return main(first,second)
},
But Jquery test suite does not pass.
It just passes up to test number 5
(ready: Error in ready callback does not halt all future executions (gh-1823) (1))
and does not pass from test 6.
I think my code has some side effects. But I cannot guess.
Is there anyone who can find it?
While the answer to your other question shows how you can write this function more simply, and while we don't have enough information to know why your test suite is failing (next time include the test that is failing), we can look at your function. Slightly reformatted, this is
const merge = function( first, second ) {
var len = +second.length,
j = 0,
i = first.length;
let main = (first,second) => {
return R.concat(first, second);
}
first.length = i;
return main(first,second)
}
Now let's simplify that. Your return statement is return main(first, second). And that main function does not depend at all upon len, j, or i, and resetting first.length to the value that was just set from first.length does nothing. So this can be just as easily written as
const merge = function( first, second ) {
let main = (first,second) => {
return R.concat(first, second);
}
return main(first,second)
}
But now note that your helper function, has nothing in its closure except for the parameters to the outer function, and those are shadowed by its own parameters. Which means that you can eliminate it entirely, and just use its body.
So we get to
const merge = function( first, second ) {
return R.concat(first, second);
}
And finally, functions of the format function(p1, p2, ..., pn) {return anotherFunc(p1, p2, ..., pn);} aren't doing anything more than renaming anotherFunc..
So in the end, you can just write
const merge = R.concat
I don't know why your function had that additional work, but it was all unnecessary. I can't see how any of it would cause the test suite to fail, as you did write a function that should still work, and there are no obvious side-effects, but perhaps the test system actually tracked the change to the length of its parameters. That would be unusual, but certainly possible.
I can't see anything from your code what might cause that error. The code relating to that test case in jQuery should be completely unrelated.
That said, one of the differences between the two functions is that the jQuery merge inserts the elements of second onto the end of the first array instance. R.concat however treats the arguments as immutable, returning a new array containing all the elements of first and second leaving the original first and second arrays unmodified. n.b. This will produce differences in behaviour if the in-place modification of the array in the jQuery function is relied upon.
On a side note, your second example can also be simplified like so:
merge: function( first, second ) {
return R.concat(first, second);
}
Or more simply:
merge: R.concat

Javascript observer or proxy without all changes going through proxy

I'm writing a subclass of arrays in Javascript to have better support for matrix operations (I know others exist, this is partially for me to re-teach myself linear algebra), and what I want is to have some properties that are reset whenever any values in the matrix are adjusted. Some calculations like the determinant are computationally intensive, and I'd like to be able to store them to avoid re-calculation, but then they need to be reset to null whenever any matrix elements are changed.
Essentially, it seems like what i want is the deprecated Array.observe(). And the replacement, proxies, seem like a lot of overhead for this one thing. As alluded to in some of the comments on Detecting Changes in a Javascript Array using the proxy object that were not directly addressed, I don't want to have to access my matrices only ever through proxies. I use a lot of handy [i][j] indexing and [mat[i], mat[j]] = [mat[j], mat[i]] in the code I've written so far.
class Matrix extends Array {
constructor() {
var args = [];
for (var i = 0; i < arguments.length; i++) {
if (Array.isArray(arguments[i])) {
args.push(new Matrix(...arguments[i]));
} else {
args.push(arguments[i]);
}
}
super(...args);
this._determinant = null;
}
determ(forceRecalculate = false) {
if (this._determinant === null || forceRecalculate) {
this.upperEchelon();
}
return this._determinant;
}
upperEchelon(reduced = false) {
//There's a lot of code here but in the process of doing this other thing
//you get 99% of the way to calculating the determinant so it does this
this._determinant = factor;
}
}
Basically, I want anything like mat[0][0] = 10 or mat.push([2,4,5]) that updates the values in the matrix to set mat._determinant = null. Or any equivalent method of flagging that it needs to be re-calculated next time it's asked for. I'm not opposed to using proxies necessarily if someone can help me figure out the implementation, I would just rather have this set-to-null-on-update property be inherent to my class functionality.
What I really want is a way to overload base methods like [] a la C# so the functions that do the updating would trigger this without changing syntax, but I've resigned myself to not having that in JS.
While a Proxy would work, it would also be pretty slow. A different approach would be for every method that needs to use the value of _determinant go through a different function first to check to see if the _determinant needs to be updated (and if so, updates it). This way, the expensive recalculation is not done every time the array changes, but only just in time for the result to be used. For example:
class Matrix extends Array {
constructor() {
var args = [];
for (var i = 0; i < arguments.length; i++) {
if (Array.isArray(arguments[i])) {
args.push(new Matrix(...arguments[i]));
} else {
args.push(arguments[i]);
}
}
super(...args);
this._determinant = null;
}
// next method is effectively a recursive deep join
// could also use toString if it doesn't interfere with anything else
getString() {
const itemsStr = this.map((item) => (
item instanceof Matrix
? item.getString()
: item
))
.join(',');
const result = '[' + itemsStr + ']';
return result;
}
getDeterm() {
const newString = this.getString();
if (newString !== this._lastString) {
this._lastString = newString;
this.upperEchelon();
}
return this._determinant;
}
upperEchelon() {
console.log('running upperEchelon');
this._determinant = Math.random();
}
}
const m = new Matrix([2, 3, 4], 5);
console.log(m.getDeterm());
// Not calculated again:
console.log(m.getDeterm());
// Mutation, next call of getDeterm will run upperEchelon:
m[0][0] = 1;
console.log(m.getDeterm());

Javascript function with no arguments and changing return values

I'm looking to write a function in Javascript that will take no arguments, but produce a different (predictable) output each time. I'll be using it for testing, so I need to be sure that I can predict the outputs, meaning that using Date.now() will not work. I'm thinking about using a seeded RNG (or PRNG), but I'm wondering if there are any alternate solutions.
Edit: the function must be self contained, no variables or data outside the scope of the function.
Just modify a variable in a wider scope.
var my_counter = 0;
function count() {
return ++my_counter;
}
console.log(count());
console.log(count());
console.log(count());
console.log(count());
console.log(count());
console.log(count());
I would go with a randomly seeded number solution that maps to whatever results you wish to return. That way you can keep it contained within the function.
OR have an array of results that can be returned and simply increment an index (with a modulus so that the index wraps back to the start again) and return the result at the indexed value. Something like...
var index = 0;
var results = [ "x", "y", "z" ];
function test() {
var value = results[index];
index = (index++) % results.length;
return value;
}
So, the function will keep looping through the x, y and z results. You can set as many predictable results as you wish.
Since via closure any function can hold state (say, your counter), you could generate any arbitrary-but-fixed data array (like, a string with the digits of Pi) and return always the next value:
var getArbitraryValue = (function () {
var i = 0;
var arbitraryValue = Math.PI.toString();
return function () {
// you may want to reset the index here, once your string runs out
return arbitraryValue.charAt(i++);
}
} ());
Repeating calls to getArbitraryValue will return the digits of Pi.
This is a fun use case for ES2015 generators and iterators, if you don't mind involving babel:
function* mygenerator() {
let i = 0;
while(true){
yield i++
}
}
const generator = mygenerator();
const noArgsDifferentResult = () => generator.next().value;
console.log(noArgsDifferentResult()); // 0
console.log(noArgsDifferentResult()); // 1
console.log(noArgsDifferentResult()); // 2
Try it out in the Babel live parser https://babeljs.io/repl/

change array passed to function

I pass 2 arrays to a function and want to move a specific entry from one array to another. The moveDatum function itself uses underscorejs' methods reject and filter. My Problem is, the original arrays are not changed, as if I was passing the arrays as value and not as reference. The specific entry is correctly moved, but as I said, the effect is only local. What do I have to change, to have the original arrays change as well?
Call the function:
this.moveDatum(sourceArr, targetArr, id)
Function itself:
function moveDatum(srcDS, trgDS, id) {
var ds = _(srcDS).filter(function(el) {
return el.uid === uid;
});
srcDS = _(srcDS).reject(function(el) {
return el.uid === uid;
});
trgDS.push(ds[0]);
return this;
}
Thanks for the help
As mentioned in the comments, you're assigning srcDS to reference a new array returned by .reject(), and thus losing the reference to the array originally passed in from outside the function.
You need to perform your array operations directly on the original array, perhaps something like this:
function moveDatum(srcDS, trgDS, id) {
var ds;
for (var i = srcDS.length - 1; i >= 0; i--) {
if (srcDS[i].uid === id) {
ds = srcDS[i];
srcDS.splice(i,1);
}
}
trgDS.push(ds);
return this;
}
I've set up the loop to go backwards so that you don't have to worry about the loop index i getting out of sync when .splice() removes items from the array. The backwards loop also means ds ends up referencing the first element in srcDS that matches, which is what I assume you intend since your original code had trgDS.push(ds[0]).
If you happen to know that the array will only ever contain exactly one match then of course it doesn't matter if you go forwards or backwards, and you can add a break inside the if since there's no point continuing the loop once you have a match.
(Also I think you had a typo, you were testing === uid instead of === id.)
Copy over every match before deleting it using methods which modify Arrays, e.g. splice.
function moveDatum(srcDS, trgDS, id) { // you pass an `id`, not `uid`?
var i;
for (i = 0; i < srcDS.length; ++i) {
if (srcDS[i].uid === uid) {
trgDS.push(srcDS[i]);
srcDS.splice(i, 1);
// optionally break here for just the first
i--; // remember; decrement `i` because we need to re-check the same
// index now that the length has changed
}
}
return this;
}

Categories