I've got 50 different lists, called list1, list2, and so forth. I also have a function which rolls a random number between 1 and 50 and then stores the value in a variable called randomNumber, and what I want to do is to access the list with a matching number.
My attempt at access the list looked like this:
document.getElementById("demo").innerHTML = list + randomNumber;
One solution would be to put all 50 lists into one list, and then use the randomNumber to access the right list through index. I am however still curious if this can be done in a way similar to what I was decribing above the code though.
Inserting the arrays into another array and accessing them by their indexes (or assigning them to property values on an object and accessing them by their associated property names) is the right approach.
The only way to reference scoped variables by strings representing their names is by using eval().
However, I will echo the linked MDN article: Don't do this.
⚠️ Warning: Executing JavaScript from a string is an enormous security risk. It is far too easy for a bad actor to run arbitrary code when you use eval(). See Never use eval()!, below.
Here's an example of using eval to reference each of the arrays below:
const list1 = [1, 2, 3];
const list2 = [4, 5, 6];
console.log(eval('list' + '1')); // [1, 2, 3]
console.log(eval('list' + '2')); // [4, 5, 6]
And here's an example of the recommended approach:
const list1 = [1, 2, 3];
const list2 = [4, 5, 6];
// As an object:
const listNames = {
list1,
list2,
};
// As an array:
const lists = [
list1,
list2,
];
console.log(listNames['list' + '1']); // [1, 2, 3]
console.log(lists[0]); // [1, 2, 3]
console.log(listNames['list' + '2']); // [4, 5, 6]
console.log(lists[1]); // [4, 5, 6]
Two days ago, I announced a preview release of Underscore that integrates with the new Node.js way of natively supporting ES modules.1 Yesterday, somebody responded on Twitter with the following question:
Can you do Ramda-style data last functions?
He or she was referring to one of the main differences between Underscore and Ramda. In Underscore, functions typically take the data to be operated on as the first parameter, while Ramda takes them as the last parameter:
import _ from 'underscore';
import * as R from 'ramda';
const square = x => x * x;
// Underscore
_.map([1, 2, 3], square); // [1, 4, 9]
// Ramda
R.map(square, [1, 2, 3]); // [1, 4, 9]
The idea behind the data-last order in Ramda is that when doing partial application, the data argument is often supplied last. Taking the data as the last parameter removes the need for a placeholder in such cases:
// Let's create a function that maps `square` over its argument.
// Underscore
const mapSquare = _.partial(_.map, _, square);
// Ramda with explicit partial application
const mapSquare = R.partial(R.map, [square]);
// Ramda, shorter notation through automatic currying
const mapSquare = R.map(square);
// Ramda with currying and placeholder if it were data-first
const mapSquare = R.map(R.__, square)
// Behavior in all cases
mapSquare([1, 2, 3]); // [1, 4, 9]
mapSquare([4, 5, 6]); // [16, 25, 36]
As the example shows, it is especially the curried notation that makes data-last attractive for such scenarios.
Why doesn't Underscore do this? There are several reasons for that, which I put in a footnote.2 Nevertheless, making Underscore behave like Ramda is an interesting exercise in functional programming. In my answer below, I'll show how you can do this in just a few lines of code.
1 At the time of writing, if you want to try it, I recommend installing underscore#preview from NPM. This ensures that you get the latest preview version. I just published a fix that bumped the version to 1.13.0-1. I will release 1.13.0 as underscore#latest some time in the near future.
2 Reasons for Underscore to not implement data-last or currying:
Underscore was born when Jeremy Ashkenas factored out common patterns from DocumentCloud (together with Backbone). As it happens, neither data-last partial application nor currying were common patterns in that application.
Changing Underscore from data-first to data-last would break a lot of code.
It is not a universal rule that data are supplied last in partial application; supplying the data first is equally imaginable. Thus, data-last isn't fundamentally better, it's just making a different tradeoff.
While currying is nice, it also has some disadvantages: it adds overhead and it fixes the arity of a function (unless you make the function lazy, which adds more overhead). Underscore works more with optional and variadic arguments than Ramda, and also prefers making features that add overhead opt-in instead of enabling them by default.
Taking the question very literally, let's just start with a function that transforms a data-first function into a data-last function:
const dataLast = f => _.restArguments(function(args) {
args.unshift(args.pop());
return f.apply(this, args);
});
const dataLastMap = dataLast(_.map);
dataLastMap(square, [1, 2, 3]); // [1, 4, 9]
We could map dataLast over Underscore to get a data-last version of the entire library:
const L = _.mapObject(_, dataLast);
const isOdd = x => x % 2;
L.map(square, [1, 2, 3]); // [1, 4, 9]
L.filter(isOdd, [1, 2, 3]); // [1, 3]
However, we can do better. Ramda-style currying is not too hard to implement, either:
const isPlaceholder = x => x === _;
function curry(f, arity = f.length, preArgs = []) {
const applied = _.partial.apply(null, [f].concat(preArgs));
return _.restArguments(function(args) {
const supplied = _.countBy(args, isPlaceholder)['false'];
if (supplied < arity) {
return curry(applied, arity - supplied, args);
} else {
return applied.apply(null, args);
}
});
}
With just a little bit of extra sophistication, we can even correctly support this bindings:
function curry(f, arity = f.length, preArgs = [], thisArg) {
if (!_.isUndefined(thisArg)) f = f.bind(thisArg);
const applied = _.partial.apply(null, [f].concat(preArgs));
return _.restArguments(function(args) {
const supplied = _.countBy(args, isPlaceholder)['false'];
if (supplied < arity) {
return curry(applied, arity - supplied, args, this);
} else {
return applied.apply(this, args);
}
});
}
Currying by itself is independent of whether you do data-first or data-last. Here's a curried version of _.map that is still data-first:
const curriedMap = curry(_.map);
curriedMap([1, 2, 3], square, null);
curriedMap([1, 2, 3])(square, null);
curriedMap([1, 2, 3])(square)(null);
curriedMap([1, 2, 3], square)(null);
curriedMap([1, 2, 3], _, null)(square);
curriedMap(_, _, null)([1, 2, 3], square);
curriedMap(_, _, null)(_, square)([1, 2, 3]);
curriedMap(_, square, _)(_, null)([1, 2, 3]);
// all [1, 4, 9]
Note that I have to pass null every time, because _.map takes an optional third argument that lets you bind the callback to a context. This eager style of currying forces you to pass a fixed number of arguments. In the Variation section below, I'll show how this can be avoided with a lazy variant of curry.
The Ramda library omits the optional context parameter instead, so you need to pass exactly two instead of exactly three arguments to R.map. We can write a function that composes dataLast and curry and that optionally adjusts the arity, in order to make an Underscore function behave exactly like its Ramda counterpart:
const ramdaLike = (f, arity = f.length) => curry(dataLast(f), arity);
const ramdaMap = ramdaLike(_.map, 2);
ramdaMap(square, [1, 2, 3]);
ramdaMap(square)([1, 2, 3]);
ramdaMap(_, [1, 2, 3])(square);
// all [1, 4, 9]
Mapping this over the entire library requires some administration in order to get a satisfying result, but the result is a surprisingly faithful imitation of Ramda:
const arityOverrides = {
map: 2,
filter: 2,
reduce: 3,
extend: 2,
defaults: 2,
// etcetera, as desired
};
const R_ = _.extend(
// start with just passing everything through `ramdaLike`
_.mapObject(_, f => ramdaLike(f)),
// then replace a subset with arity overrides
_.mapObject(arityOverrides, (arity, name) => ramdaLike(_[name], arity)),
);
R_.identity(1); // 1
R_.map(square)([1, 2, 3]); // [1, 4, 9]
R_.filter(isOdd)([1, 2, 3]); // [1, 3]
const add = (a, b) => a + b;
const sum = R_.reduce(add, 0);
sum([1, 2, 3]); // 6
Variation
At the cost of introducing laziness, we can avoid having to fix the arity of a function. This lets us preserve all the optional and variadic parameters from the original Underscore functions, without always needing to supply them, and removes the need for per-function administration when mapping the library. We start with a variant of curry that returns a lazy function instead of an eager one:
function curryLazy(f, preArgs = [], thisArg) {
if (!_.isUndefined(thisArg)) f = f.bind(thisArg);
const applied = _.partial.apply(null, [f].concat(preArgs));
return _.restArguments(function(args) {
if (args.length > 0) {
return curryLazy(applied, args, this);
} else {
return applied.call(this);
}
});
}
This is basically R.curry with a builtin R.thunkify on top. Note that this implementation is actually a bit simpler than the eager variant. On top of that, creating a lazy, Ramda-like port of Underscore is reduced to an elegant oneliner:
const LR_ = _.mapObject(_, _.compose(curryLazy, dataLast));
We can now pass as many or as few arguments to each function as we want. We just have to append an extra call without arguments in order to force evaluation:
LR_.identity(1)(); // 1
LR_.map([1, 2, 3])(); // [1, 2, 3]
LR_.map(square)([1, 2, 3])(); // [1, 4, 9]
LR_.map(_, [1, 2, 3])(square)(); // [1, 4, 9]
LR_.map(Math.sqrt)(Math)([1, 4, 9])(); // [1, 2, 3]
LR_.filter([1, false, , '', 'yes'])(); // [1, 'yes']
LR_.filter(isOdd)([1, 2, 3])(); // [1, 3]
LR_.filter(_, [1, 2, 3])(isOdd)(); // [1, 3]
LR_.filter(window.confirm)(window)([1, 2, 3])(); // depends on user
LR_.extend({a: 1})({a: 2, b: 3})();
// {a: 1, b: 3}
LR_.extend({a: 1})({a: 2, b: 3})({a: 4})({b: 5, c: 6})();
// {a: 4, b: 3, c: 6}
This trades some faithfulness to Ramda for faithfulness to Underscore. In my opinion, it is a best of both worlds: data-last currying like in Ramda, with all the parametric flexibility from Underscore.
References:
Underscore documentation
Ramda documentation
Is there a way to get the array being generated by the map function, inside the map function? For e.g.
let arr = [1, 2, 3, 4];
arr.map(item => {
console.log(currentArray);
return item * item;
}
So, in the above example, if at line 3 (currentArray), I want below output, how can I get that.
[1]
[1, 4]
[1, 4, 9]
The "array" parameter in map callback, returns the original array on which map function is called.
Or do I need to go conventional for loop route to achieve this?
You could map the squares by using a closure over ta temporary result set.
let array = [1, 2, 3, 4],
result = array.map((t => v => t = [...t, v * v])([]));
console.log(result);
You can use reduce function as follow,
let arr = [1, 2, 3, 4];
arr.reduce((acc, item)=>{
acc.push(item*item);
console.log(acc);
return acc;
}, []);
// SINCE YOU ARE ASKING HOW TO DO IT USING MAP
console.log('*******************************')
let arr1 = [1, 2, 3, 4];
const currentArray = [];
arr1.map(item => {
currentArray.push(item * item);
console.log(currentArray);
return item* item;
})
You can use the reduce instead of the map, but you do not have to by the way. Anyways, then you will have your accumulator as the first argument to the reducer callback. The variable accumulator will hold the intermediate states of the value to be returned.
let arr = [1, 2, 3, 4];
let result = arr.reduce((accumulator, item) => {
console.log("currentArray accumulator: ", accumulator);
return [...accumulator, item * item];
}, []);
There is a neat another answer around which uses map and do what you want. So, yes it is possible to achieve same with map and reduce is a convenience function that actually is a special case map function itself.
I want to take an array [1, 2, 3] and return [1, 2, 3, 1].
I'm using Ramda, and I can get the desired result like this:
const fn = arr => R.append(R.prop(0, arr), arr);
But I'd like to do it point-free. Here's the closest I've gotten:
const fn = R.compose(R.append, R.prop(0));
fn(arr)(arr)
But that looks silly. What am I missing? Thanks!
converge can be very helpful for things like this.
const rotate = R.converge(R.append, [R.head, R.identity])
rotate([1, 2, 3]); //=> [1, 2, 3, 1]
The S combinator is useful here:
S.S(S.C(R.append), R.head, [1, 2, 3]);
// => [1, 2, 3, 1]
How do you efficiently scroll an array with looping either acting on the array itself or returning a new array
arr = [1,2,3,4,5]
i want to do something like this:
arr.scroll(-2)
arr now is [4,5,1,2,3]
Use Array.slice:
> arr.slice(-2).concat(arr.slice(0, -2));
[4, 5, 1, 2, 3]
You can then generalize it and extend Array.prototype with the scroll function:
Array.prototype.scroll = function (shift) {
return this.slice(shift).concat(this.slice(0, shift));
};
> arr.scroll(-2);
[4, 5, 1, 2, 3]