Related
I was working with an array in JavaScript and was wondering why changes I made to an array were correctly saving to localStorage, but weren't being reflected in the array past the function call. The code was the following:
function removeFromList(array, arrayName, key) {
array = array.filter(function(element) { return element.key !== key; });
localStorage.setItem(arrayName, JSON.stringify(array));
}
I did some googling and, through some old posts, discovered that the array was being passed by value to the function, that is, the array variable, which pointed to an array object, was being passed by value, and changing that copy did not affect the original variable that was pointing to my array object.
I came up with the following code as a workaround:
function removeFromList(array, arrayName, key) {
arrayTemp = array.filter(function(element) { return element.key !== key; });
for(var i = 0; i < array.length; i++) {
if (!arrayTemp.some(item => item.key === array[i].key)) {
array.splice(i, 1);
}
}
localStorage.setItem(arrayName, JSON.stringify(array));
}
This solved my problem, and the new contents of the array was displayed in both localStorage and the array object that was pointed to by the original variable. However, I've been wondering if there is some new way introduced into JavaScript recently or an older method I did not find that would do a better job of achieving the desired result.
I did some googling and, through some old posts, discovered that the array was being passed by value to the function, that is, the array variable, which pointed to an array object, was being passed by value, and changing that copy did not affect the original variable that was pointing to my array object.
Exactly right.
Is there a way to change the original reference to an object in a function call
No, JavaScript is still a purely pass-by-value language. While I suppose it's possible for that to change at some point, it hasn't as of this writing (and it seems really unlikely to me it ever will). If you do example(x) when x contains 42 (or an array reference), there still isn't any way for example to reach out and change the value of x (to 43, or to refer to a different array). If x refers to a mutable object (like an array), example can modify that object, but it can't make x refer to a whole new object.
Your workaround works by modifying the existing array. FWIW, in general it would be preferred to return the new array instead, so the caller has the option of either keeping the original or using the new one. E.g.:
function removeFromList(array, arrayName, key) {
array = array.filter(function(element) { return element.key !== key; });
localStorage.setItem(arrayName, JSON.stringify(array));
return array;
}
And then when using it:
variableContainingArray = removeFromList(variableContainingArray, "nameInLocalStorage", 42);
But if you want to update in place, you don't need a temporary array:
function removeFromList(array, arrayName, key) {
// Looping backward we don't have to worry about the indexes when we remove an entry
for (let i = array.length - 1; i >= 0; --i) {
if (array[i].key === key) {
array.splice(i, 1);
}
}
localStorage.setItem(arrayName, JSON.stringify(array));
}
Instead of using a for-loop to remove the values from the argument array you can also empty it out using splice and add the filtered values:
function removeFromList(array, arrayName, key) {
var filtered = array.filter(function(element) { return element.key !== key; });
array.splice(0, array.length, ...filtered);
localStorage.setItem(arrayName, JSON.stringify(array));
}
I suggest changing var to const and function(element) { return element.key !== key; } to element => element.key !== key if those features are available within your runtime environment.
I'm trying to use the factorial function with memoization. I have taken the max value from the object to reduce the number of recursive calls made. But the problem is the first call is I don't know whether this is optimized or not since the first call is pretty expensive. Any insights on this will be great.
let cache = {0: 1};
function factMemoize(key) {
if (!cache[key]) {
let maxVal = Object.keys(cache).reduce(function (a, b) {
return Math.max(a, b);
});
console.log(maxVal);
while (key >= maxVal) {
cache[key] = key * factMemoize(key - 1);
maxVal++;
}
}
return cache[key];
}
You don't buy much from memoizing this since you only use each value once. After you've called the function you do have the cache for a second call, but we often think of memoizing as something that happens in a cache that only exists during the function. For something like that, calculating Fibonacci numbers is a classic example where memoizing is a huge improvement over the naive recursive function.
Having said that, in your function it's not clear why you are using an object for your cache and then searching it. You can just use an array where the indexes will be the number you're looking for calculate. You don't need to search it, just start with the number and recursively call the next lower one. If there's a cache it, it will return. For example:
let cache = [1];
function factMemoize(key) {
if (!cache[key]) {
cache[key] = key * factMemoize(key - 1)
} else { // just to demo cache:
console.log("cache hit:", key)
}
return cache[key]
}
// only hits cache at the end
console.log("6! = ", factMemoize(6))
// second call benefits from cache:
console.log("8! = ", factMemoize(8))
Factorial Memoization using Closure
As #mark-meyer mentioned in this thread, there is no advantage coming from memoizing the results since each value will be calculated only one time during computation. The solution Mark offered is great for reusing the function in a later time by re-calling factorials with same or different values. In that case, you can speed up the process and reduce the time complexity by reusing existing results.
Here is how it can look like in a closure:
function factorialFn() {
const cache = [];
return function _factorial() {
if (n < 2) {
return 1;
}
if (cache[n]) {
return cache[n];
}
return cache[n] = n * _factorial(n - 1);
}
}
Then, you can use it like so:
const factorial = factorialFn();
factorial(5); // 120
factorial(7); // 5040
At the first call, it will calculate `factorial(5)` and save it in cache for future reference.
At the second call, `factorial(7)` will execute `7 * factorial(6)`, which `factorial(6)` is basically `6 * the cached value of factorial(5)`
let cache = {};
function factorial(n) {
if (n < 2) return 1;
if (n in cache) return cache[n];
return cache[n] = n * factorial(n - 1);
}
factorial(5); // it will store values in cache from 2-5
factorial(7); // for this it would not compute till 5(from cache)
I'm following an online course about Javascript Functional Programming
at the Exercise 16 it show you how reduce is actually implemented, in order to help you understand how to use it, but into this implementation there is something i don't actually get, i'll show the code:
Array.prototype.reduce = function(combiner, initialValue) {
var counter, accumulatedValue;
// If the array is empty, do nothing
if (this.length === 0) {
return this;
}
else {
// If the user didn't pass an initial value, use the first item.
if (arguments.length === 1) {
counter = 1;
accumulatedValue = this[0];
}
else if (arguments.length >= 2) {
counter = 0;
accumulatedValue = initialValue;
}
else {
throw "Invalid arguments.";
}
// Loop through the array, feeding the current value and the result of
// the previous computation back into the combiner function until
// we've exhausted the entire array and are left with only one value.
while(counter < this.length) {
accumulatedValue = combiner(accumulatedValue, this[counter])
counter++;
}
return [accumulatedValue];
}
};
I don't understand the first if statement, when it check for this.length what this actually mean?
Take note this is different from the reduce in ES5, which returns an value instead of an Array, this is used just as a sample for the learning purpose.
Array.prototype.reduce = function(...
is saying, "create a function on the prototype of Array" - this means that the new reduce function will be callable on all arrays, eg:
[1, 2, 3].reduce(...
This means you can also call it on empty arrays, eg:
[].reduce(...
Building on the comment:
If the array is empty, do nothing
You're working on an array, and when the function is called, this is set to the array that reduce was called on. This implementation of reduce assumes that if that array is empty (ie this.length === 0), you can't logically reduce it any further - there's nothing to reduce, so you can return the same empty array.
As pointed out by #Alnitak in the comments, this implementation of reduce is flawed as compared to the specification. A different implementation is available on the MDN for polyfilling older browsers.
For education purposes, I was trying to re-create Underscore.js's _.reduce() method. While I was able to do this in an explicit style using for loops. But this is far from ideal because it mutates the original list that was supplied as an argument, which is dangerous.
I also realized that creating such method using functional programming style is harder, since it is not possible to explicitly set i value for looping.
// Explicit style
var reduce = function(list, iteratee, initial) {
if (Array.isArray(list)) {
var start;
if (arguments.length === 3) {
start = initial;
for (var i = 0; i < list.length; i++) {
start = iteratee(start, list[i], i);
}
} else {
start = list[0];
for (var i = 1; i < list.length; i++) {
start = iteratee(start, list[i], i);
}
}
}
if (list.constructor === Object) {
var start;
if (arguments.length === 3) {
start = initial;
for (var key in list) {
start = iteratee(start, list[key], key);
}
} else {
start = list[Object.keys(list)[0]];
// Delete the first property to avoid duplication.
delete list[Object.keys(list)[0]];
for (var key in list) {
start = iteratee(start, list[key], key);
}
}
}
return start;
};
What makes me struggle is that when my reduce() is supplied with an argument, initial, I need to subsequently either skip or remove the first element or property of the argument, list for the final value that will be returned. Because not doing so will double count the first element/property. I can't think of how I could do such thing when creating the function with functional programming style, with _.each() or forEach() involved.
This is my functional style of reduce() that is working partially. It works correctly when memo(initial value) is supplied, because I don't need to skip the first element/property. But it's not working correctly when memo is not supplied, because then I'm setting memo to either first element or property, and I should be able to skip it during the looping, which I don't know how.
// Functional style (not working without memo)
var reduce = function(list, iteratee, memo) {
var memo = memo || list[0] || list[Object.keys(list)[0]];
_.each(list, function(element, index, list){
memo = iteratee(memo, element, index, list);
});
return memo;
};
I spent quite a long time searching for answers to my question on Google. But wasn't able to find one. I would really appreciate your advice. Thanks.
Lastly, this is an additional code I came up with which does not work, but I think it should.
var reduce = function(list, iteratee, memo) {
var collection = list;
var accumulation;
_.each(collection, function(item){
if (arguments.length < 3) {
if (Array.isArray(collection)) {
accumulation = collection[0];
collection.shift();
accumulation = iteratee(accumulation, item);
} else {
accumulation = collection[Object.keys(collection)[0]];
delete collection[Object.keys(collection)[0]];
accumulation = iteratee(accumulation, item);
}
} else {
accumulation = memo;
accumulation = iteratee(accumulation, item);
}
});
return accumulation;
};
Here is the shortest version I could come up with.
_.reduce = function(list, iteratee, memo){
var memoUndefined = arguments.length < 3;
_.each(list, function(elem, index, list){
if(memoUndefined) {
memoUndefined = false;
memo = elem;
} else memo = iteratee(memo, elem, index, list);
});
return memo;
};
Reduce accepts three parameters: A collection (array or object), callback, and accumulator (this one is optional).
Reduce iterates through the collection, which invokes the callback and keeps track of the result in the accumulator.
If an accumulator is not passed in, we'll set it to the first element of the collection.
If an accumulator is available, we'll set the accumulator to be equal to the result of invoking the callback and passing in the current accumulator and the current element of the collection. Remember: Javascript executes its operations in right-to-left order, meaning the right side of the operator occurs first before it gets assigned to the variable on the left.
_.reduce = function(collection, callback, accumulator){
_.each(collection, function(elem){
return accumulator === undefined ? accumulator = collection[0] : accumulator = callback(accumulator, elem);
});
return accumulator;
};
First, you need a way to determine whether reduce received an initial memo value when you are inside the function you pass to _.each. You could do this a number of ways. One way is to simply set a flag based on the length of arguments. You need to do this outside the _.each call because the function you pass to _.each will have its own arguments object, masking the arguments object for reduce.
Using your code as a starting point:
var reduce = function(list, iteratee, memo) {
var considerFirst = arguments.length > 2;
var memo = memo || list[0] || list[Object.keys(list)[0]];
_.each(list, function(element, index, list){
if (index > 0 || considerFirst) {
memo = iteratee(memo, element, index, list);
}
});
return memo;
};
This still isn't quite right, though. We also need to update how you are defaulting memo. Currently, if memo receives a falsy value (e.g. 0), we still set it to the fist element in the list, but we don't set the flag indicating to ignore the first element. This means reduce will process the first element twice.
To get this right, you need to change how you are defaulting memo, setting it only if no argument is passed in. You could do something like this:
var reduce = function(list, iteratee, memo) {
var considerFirst = true;
if (arguments.length < 3) {
memo = list[0];
considerFirst = false;
}
_.each(list, function(element, index, list){
if (index > 0 || considerFirst) {
memo = iteratee(memo, element, index, list);
}
});
return memo;
};
This way, you only set memo if no argument was passed.
Note that you don't need to initialize memo with var. Having memo as a parameter does all the initialization you need.
Also note that I removed support for using reduce on a plain object. When you pass an object to _.each, the value of the index parameter is not a numerical index but the key for that entry, which may or may not be an integer. This does not play well with our index > 0 check to see if we are looking at the first entry. There are ways around this, but it doesn't seem central to your question. Check out the actual underscore implementation if you want to see how to make it work.
Update: the implementation SpiderPig suggests doesn't rely on index and so would work with objects, not just arrays.
Lastly, it's worth pointing out that underscore's implementation of _.reduce uses a for loop and not _.each.
First, let me define what is short-cut fusion for those of you who don't know. Consider the following array transformation in JavaScript:
var a = [1,2,3,4,5].map(square).map(increment);
console.log(a);
function square(x) {
return x * x;
}
function increment(x) {
return x + 1;
}
Here we have an array, [1,2,3,4,5], whose elements are first squared, [1,4,9,16,25], and then incremented [2,5,10,17,26]. Hence, although we don't need the intermediate array [1,4,9,16,25], we still create it.
Short-cut fusion is an optimization technique which can get rid of intermediate data structures by merging some functions calls into one. For example, short-cut fusion can be applied to the above code to produce:
var a = [1,2,3,4,5].map(compose(square, increment));
console.log(a);
function square(x) {
return x * x;
}
function increment(x) {
return x + 1;
}
function compose(g, f) {
return function (x) {
return f(g(x));
};
}
As you can see, the two separate map calls have been fused into a single map call by composing the square and increment functions. Hence the intermediate array is not created.
Now, I understand that libraries like Immutable.js and Lazy.js emulate lazy evaluation in JavaScript. Lazy evaluation means that results are only computed when required.
For example, consider the above code. Although we square and increment each element of the array, yet we may not need all the results.
Suppose we only want the first 3 results. Using Immutable.js or Lazy.js we can get the first 3 results, [2,5,10], without calculating the last 2 results, [17,26], because they are not needed.
However, lazy evaluation just delays the calculation of results until required. It does not remove intermediate data structures by fusing functions.
To make this point clear, consider the following code which emulates lazy evaluation:
var List = defclass({
constructor: function (head, tail) {
if (typeof head !== "function" || head.length > 0)
Object.defineProperty(this, "head", { value: head });
else Object.defineProperty(this, "head", { get: head });
if (typeof tail !== "function" || tail.length > 0)
Object.defineProperty(this, "tail", { value: tail });
else Object.defineProperty(this, "tail", { get: tail });
},
map: function (f) {
var l = this;
if (l === nil) return nil;
return cons(function () {
return f(l.head);
}, function () {
return l.tail.map(f);
});
},
take: function (n) {
var l = this;
if (l === nil || n === 0) return nil;
return cons(function () {
return l.head;
}, function () {
return l.tail.take(n - 1);
});
},
mapSeq: function (f) {
var l = this;
if (l === nil) return nil;
return cons(f(l.head), l.tail.mapSeq(f));
}
});
var nil = Object.create(List.prototype);
list([1,2,3,4,5])
.map(trace(square))
.map(trace(increment))
.take(3)
.mapSeq(log);
function cons(head, tail) {
return new List(head, tail);
}
function list(a) {
return toList(a, a.length, 0);
}
function toList(a, length, i) {
if (i >= length) return nil;
return cons(a[i], function () {
return toList(a, length, i + 1);
});
}
function square(x) {
return x * x;
}
function increment(x) {
return x + 1;
}
function log(a) {
console.log(a);
}
function trace(f) {
return function () {
var result = f.apply(this, arguments);
console.log(f.name, JSON.stringify([...arguments]), result);
return result;
};
}
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
As you can see, the function calls are interleaved and only the first three elements of the array are processed, proving that the results are indeed computed lazily:
square [1] 1
increment [1] 2
2
square [2] 4
increment [4] 5
5
square [3] 9
increment [9] 10
10
If lazy evaluation is not used then the result would be:
square [1] 1
square [2] 4
square [3] 9
square [4] 16
square [5] 25
increment [1] 2
increment [4] 5
increment [9] 10
increment [16] 17
increment [25] 26
2
5
10
However, if you see the source code then each function list, map, take and mapSeq returns an intermediate List data structure. No short-cut fusion is performed.
This brings me to my main question: do libraries like Immutable.js and Lazy.js perform short-cut fusion?
The reason I ask is because according to the documentation, they “apparently” do. However, I am skeptical. I have my doubts whether they actually perform short-cut fusion.
For example, this is taken from the README.md file of Immutable.js:
Immutable also provides a lazy Seq, allowing efficient chaining of collection methods like map and filter without creating intermediate representations. Create some Seq with Range and Repeat.
So the developers of Immutable.js claim that their Seq data structure allows efficient chaining of collection methods like map and filter without creating intermediate representations (i.e. they perform short-cut fusion).
However, I don't see them doing so in their code anywhere. Perhaps I can't find it because they are using ES6 and my eyes aren't all too familiar with ES6 syntax.
Furthermore, in their documentation for Lazy Seq they mention:
Seq describes a lazy operation, allowing them to efficiently chain use of all the Iterable methods (such as map and filter).
Seq is immutable — Once a Seq is created, it cannot be changed, appended to, rearranged or otherwise modified. Instead, any mutative method called on a Seq will return a new Seq.
Seq is lazy — Seq does as little work as necessary to respond to any method call.
So it is established that Seq is indeed lazy. However, there are no examples to show that intermediate representations are indeed not created (which they claim to be doing).
Moving on to Lazy.js we have the same situation. Thankfully, Daniel Tao wrote a blog post on how Lazy.js works, in which he mentions that at its heart Lazy.js simply does function composition. He gives the following example:
Lazy.range(1, 1000)
.map(square)
.filter(multipleOf3)
.take(10)
.each(log);
function square(x) {
return x * x;
}
function multipleOf3(x) {
return x % 3 === 0;
}
function log(a) {
console.log(a);
}
<script src="https://rawgit.com/dtao/lazy.js/master/lazy.min.js"></script>
Here the map, filter and take functions produce intermediate MappedSequence, FilteredSequence and TakeSequence objects. These Sequence objects are essentially iterators, which eliminate the need of intermediate arrays.
However, from what I understand, there is still no short-cut fusion taking place. The intermediate array structures are simply replaced with intermediate Sequence structures which are not fused.
I could be wrong, but I believe that expressions like Lazy(array).map(f).map(g) produce two separate MappedSequence objects in which the first MappedSequence object feeds its values to the second one, instead of the second one replacing the first one by doing the job of both (via function composition).
TLDR: Do Immutable.js and Lazy.js indeed perform short-cut fusion? As far as I know they get rid of intermediate arrays by emulating lazy evaluation via sequence objects (i.e. iterators). However, I believe that these iterators are chained: one iterator feeding its values lazily to the next. They are not merged into a single iterator. Hence they do not “eliminate intermediate representations“. They only transform arrays into constant space sequence objects.
I'm the author of Immutable.js (and a fan of Lazy.js).
Does Lazy.js and Immutable.js's Seq use short-cut fusion? No, not exactly. But they do remove intermediate representation of operation results.
Short-cut fusion is a code compilation/transpilation technique. Your example is a good one:
var a = [1,2,3,4,5].map(square).map(increment);
Transpiled:
var a = [1,2,3,4,5].map(compose(square, increment));
Lazy.js and Immutable.js are not transpilers and will not re-write code. They are runtime libraries. So instead of short-cut fusion (a compiler technique) they use iterable composition (a runtime technique).
You answer this in your TLDR:
As far as I know they get rid of intermediate arrays by emulating lazy
evaluation via sequence objects (i.e. iterators). However, I believe
that these iterators are chained: one iterator feeding its values
lazily to the next. They are not merged into a single iterator. Hence
they do not "eliminate intermediate representations". They only
transform arrays into constant space sequence objects.
That is exactly right.
Let's unpack:
Arrays store intermediate results when chaining:
var a = [1,2,3,4,5];
var b = a.map(square); // b: [1,4,6,8,10] created in O(n)
var c = b.map(increment); // c: [2,5,7,9,11] created in O(n)
Short-cut fusion transpilation creates intermediate functions:
var a = [1,2,3,4,5];
var f = compose(square, increment); // f: Function created in O(1)
var c = a.map(f); // c: [2,5,7,9,11] created in O(n)
Iterable composition creates intermediate iterables:
var a = [1,2,3,4,5];
var i = lazyMap(a, square); // i: Iterable created in O(1)
var j = lazyMap(i, increment); // j: Iterable created in O(1)
var c = Array.from(j); // c: [2,5,7,9,11] created in O(n)
Note that using iterable composition, we have not created a store of intermediate results. When these libraries say they do not create intermediate representations - what they mean is exactly what is described in this example. No data structure is created holding the values [1,4,6,8,10].
However, of course some intermediate representation is made. Each "lazy" operation must return something. They return an iterable. Creating these is extremely cheap and not related to the size of the data being operated on. Note that in short-cut fusion transpilation, an intermediate representation is also made. The result of compose is a new function. Functional composition (hand-written or the result of a short-cut fusion compiler) is very related to Iterable composition.
The goal of removing intermediate representations is performance, especially regarding memory. Iterable composition is a powerful way to implement this and does not require the overhead that parsing and rewriting code of an optimizing compiler which would be out of place in a runtime library.
Appx:
This is what a simple implementation of lazyMap might look like:
function lazyMap(iterable, mapper) {
return {
"##iterator": function() {
var iterator = iterable["##iterator"]();
return {
next: function() {
var step = iterator.next();
return step.done ? step : { done: false, value: mapper(step.value) }
}
};
}
};
}