Multiple statements in Javascript anonymous function - javascript

(Didn't find this Q while searching, but might be a matter of not knowing the terms for which to search)
Trying to mimic range() using code I found on SO (learning the language, so avoiding using ready-made functions in underscore.js), and I'm having trouble understanding why this will work:
var range = Array.apply(null,Array(3));
range = range.map(function (x, i) {return i;});
But this does not:
var range = Array.apply(null,Array(3));
range = range.map(function (x, i) {
console.log(range[i-1]);
return i;
});
< undefined
< undefined
< undefined
Ostensibly only difference is the console.log line, but why should that matter?
Thanks in advance!

The first line is creating an array of size 3, but the values will all be undefined. When you iterate it with map (to make a new range), you're logging by indexing; range[x - i] in the first loop is looking at range[undefined - 0] (becomes range[0]), and subsequent loops look at range[-1], etc. Of course they're undefined. Until the map completes, range has not been reassigned, so all you'll ever get is undefined. When the map completes though, with or without the log, range will be [0, 1, 2].

Related

using map() filter() and reduce() to sort an array and find the square of only positive integers

I am going through a boot camp at freeCodeCamp and am stuck on a problem, trying to use other resources than the 'get hint' option they provide.
I am currently tasked with writing a function that finds all the positive integers in an array, squares them, then returns the new array. I do know what each map, filter, and reduce do, but seem to be struggling with how they get implemented. Here is the code from before I started working...
const squareList = arr => {
// Only change code below this line
return arr;
// Only change code above this line
};
const squaredIntegers = squareList([-3, 4.8, 5, 3, -3.2]);
console.log(squaredIntegers);
and the code after I added my own spin...
const squareList = arr => {
// Only change code below this line
arr.filter(arr => arr.length > 0)
arr.map(x => x * 2)
return arr
// Only change code above this line
};
const squaredIntegers = squareList([-3, 4.8, 5, 3, -3.2]);
console.log(squaredIntegers);
so after running the code I put in, I at least don't get an error, it just gives me back the same array as what is input. it doesn't even sort out just the positive integers, so I'm quite confused on what I need to do. These challenges thus far have not needed any elaborate code, most solutions have been just a few lines of simple code but as a beginner I'm trying to get better at knowing what to call.
I have consulted documentation on developer.mozilla.org to try and get the right syntax, which I think has helped in providing an actual array instead of an error, but I could be wrong.
also, since this is a challenge, they do not want me to use 'for', 'while' or 'forEach()'.
Any help is appreciated, thank you!
EDIT: I want to add, as I was writing this question I tried a few different things like changing line 3 of my code to say...
squareList.filter(squareList = > squareList.length > 0)
just to test it out, and it did not work. If I were to guess, its not that I'm calling the wrong thing, it's that I'm calling the right thing in the wrong way.
EDIT 2: Thanks for the help everyone, it does make more sense. It seems I was about halfway there, I was calling the correct objects, but not filtering them properly. I also needed to make sure I returned the changed array instead of assuming it just returned it. I had spaced to use 'Math.round' to ensure it only used integers instead of squaring everything, and using '(x => x * 2) was just bad, I should have done (x => x * x) thats just me not thinking clearly. I did end up solving the challenge, thanks so much!
I'm not sure I understand the limitations because they only state write below and above lines I would use anyway. But this works, much like your code, only some minor tweaks.
const squareList = arr => {
// Only change code below this line
arr = arr.filter(item => item > 0 && Math.round(item) == item).map(item => item * item).sort((a, b) => a - b);
return arr;
// Only change code above this line
};
const squaredIntegers = squareList([-3, 4.8, 5, 3, -3.2]);
console.log(squaredIntegers);

How can I get all combinations of a String with the given replacements in JavaScript?

I would like to return all possible combinations for a string while maintaining the proper order of everything and avoiding duplicates. The reason for this? I'd like to make answers for some Japanese quizzes more flexible by allowing a mix of kana and kanji. As such, I require all possible combinations for comparison against the user's answer.
This is the current syntax of the function: (located here)
Genki.getAlts('{月曜日}と{水曜日}と{金曜日}に{日本語}のクラスがあります', 'げつようび|すいようび|きんようび|にほんご');
The text within curly braces is the text that will be replaced by the alternative text in the second argument, I'll refer to these simply as replacements. HOWEVER, the alternate text should ONLY replace the same index. That is:
月曜日 can only be replaced with げつようび
水曜日 can only be replaced with すいようび
and so on...
To give a simple example of what I'd like to achieve. Say I have the following:
Genki.getAlts('...{A}...{B}...', '1|2', true);
I'd like it to return all combinations, such as below.
'...1...{B}...'
'...1...2...'
'...{A}...2...'
'...{A}...{B}...'
The current implementation works well with 2-7 given replacements, but when given more than 8, the total combo coverage begins to drop. The total amount of combinations can be calculated using this formula: Math.pow(2, 8), which would return "256" combinations for 8 replacements, but currently getAlts() is only returning 234 combos, which means we're missing 22, only giving us 91% combo coverage.
So that is where I'm currently stuck. You can review the current code via the links below. (and yes, it's rather hackish) Being self-taught I tried my best to get as many combos as possible, but I'm afraid that my skill with mathematics isn't that good. I'm sure there's a much simpler way of going about this and I'm just overthinking it.
code: Genki.getAlts()
test page: lesson-4/workbook-6 || page source (the console will show all current combinations)
As an example of the current algorithm's failure, open your console, and you should see a warning for the last problem, saying something along the lines of:
234/256 (91.40625% combo coverage for 8 replacements; 22 missing combos
Code for this problem:
Genki.getAlts('{1:私}はきのう{学校}で{1:写真}を{1:撮}りました。{2:私}は{家}でも{2:写真}を{2:撮}りました。', 'わたし|がっこう|しゃしん|と|わたし|いえ|しゃしん|と', true);
and a much simpler one with 10 replacements for performing test cases in the console:
Genki.getAlts('{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}', '1|2|3|4|5|6|7|8|9|10', true)
Is there any possible and simplistic way of returning all the combinations for a string regardless of how many replacements are specified? While I do know how many combinations there are, using Math.pow(2, n), I'm unsure of how to properly get them all.
I am open to hearing about existing algorithms or frameworks for achieving this.
PS: as things are, the algorithm works fine for 2-7 replacements, with very few problems ever reaching or going above this threshold. However, when they do, there's a chance that the user's answer will erroneously be marked wrong and I'd like to avoid this. The simplest solution would obviously be to avoid ever breaking 7, but that's not always possible, and furthermore, the current way I'm achieving this isn't optimal, so I would like to optimize it as well.
You can solve this problem using binary math. Here's an approach that generates the array of strings:
function getAlts(str, alt) {
var subs = alt.split('|');
var length = subs.length;
var permutations = Math.pow(2, length);
var results = [];
for (var i = 0; i < permutations; ++i) {
var bitIndex = 0;
var result = str.replace(/\{(.*?)\}/g, function (match, p1) {
var subIndex = bitIndex++;
var bit = length - 1 - subIndex;
return ((1 << bit) & i) ? subs[subIndex] : p1;
});
results.push(result);
}
return results;
}
console.log(getAlts('...{A}...{B}...', '1|2'));
Or if you're able to use ES6 (ECMAScript 2015), you can write a generator function to use less memory:
function* getAlts(str, alt) {
var subs = alt.split('|');
var length = subs.length;
var permutations = Math.pow(2, length);
for (var i = 0; i < permutations; ++i) {
var bitIndex = 0;
var result = str.replace(/\{(.*?)\}/g, function (match, p1) {
var subIndex = bitIndex++;
var bit = length - 1 - subIndex;
return ((1 << bit) & i) ? subs[subIndex] : p1;
});
yield result;
}
}
var results = getAlts('{A}{B}{C}{D}{E}{F}{G}{H}{I}', '1|2|3|4|5|6|7|8|9');
var total = 0;
for (var result of results) {
console.log(result);
total++;
}
console.log('total:', total);

Why are negative array indices much slower than positive array indices?

Here's a simple JavaScript performance test:
const iterations = new Array(10 ** 7);
var x = 0;
var i = iterations.length + 1;
console.time('negative');
while (--i) {
x += iterations[-i];
}
console.timeEnd('negative');
var y = 0;
var j = iterations.length;
console.time('positive');
while (j--) {
y += iterations[j];
}
console.timeEnd('positive');
The first loop counts from 10,000,000 down to 1 and accesses an array with a length of 10 million using a negative index on each iteration. So it goes through the array from beginning to end.
The second loop counts from 9,999,999 down to 0 and accesses the same array using a positive index on each iteration. So it goes through the array in reverse.
On my PC, the first loop takes longer than 6 seconds to complete, but the second one only takes ~400ms.
Why is the second loop faster than the first?
Because iterations[-1] will evaluate to undefined (which is slow as it has to go up the whole prototype chain and can't take a fast path) also doing math with NaN will always be slow as it is the non common case.
Also initializing iterations with numbers will make the whole test more useful.
Pro Tip: If you try to compare the performance of two codes, they should both result in the same operation at the end ...
Some general words about performance tests:
Performance is the compiler's job these days, code optimized by the compiler will always be faster than code you are trying to optimize through some "tricks". Therefore you should write code that is likely by the compiler to optimize, and that is in every case, the code that everyone else writes (also your coworkers will love you if you do so). Optimizing that is the most benefitial from the engine's view. Therefore I'd write:
let acc = 0;
for(const value of array) acc += value;
// or
const acc = array.reduce((a, b) => a + b, 0);
However in the end it's just a loop, you won't waste much time if the loop is performing bad, but you will if the whole algorithm performs bad (time complexity of O(n²) or more). Focus on the important things, not the loops.
To elaborate on Jonas Wilms' answer, Javascript does not work with negative indice (unlike languages like Python).
iterations[-1] is equal to iteration["-1"], which look for the property named -1 in the array object. That's why iterations[-1] will evaluate to undefined.

javascript - Issue in the loop in algorithm exercise

I'm studing an algorithm problem and I did understand all the things except the lines that I marked up with comment into the code.
Note: It is not to solve the code. The code is working good. It is only to explain me about what is the marked lines' purposes.
let arr= [40, 50, 80, 20, 21, 35]
function qSort(arr) {
if(arr.length == 0){ // <-- These lines. I did NOT understand its purpose
return [];
}
var left = []
var right = []
var pivot = arr[0]
for(var i= 1; i<arr.length; i++) {
if(arr[i] < pivot) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return qSort(left).concat(pivot, qSort(right))
}
console.log(qSort(arr))
This code is working good, but When I comment these lines into the code, the script doesn't work and it cause an error message referencing a loop issue: Uncaught RangeError: Maximum call stack size exceeded
Does anybody explain it to me please? I would like to know what is this lines' purpose. Thanks
If the given array has a length of 0 (is empty), just return an empty array, because there is no sorting to be done.
Actually the line could be:
if(arr.length <= 1){
return arr;
}
since for 0 and 1 elements the's no sorting to be done.
This is related to the function being recursive (it calls itself) and therefor it needs a case, at wich it stops calling itself and return a value. That's called a recursion anchor.
This is called the base case of recursion, which ends the recursion. If you leave it away, qSort will call qSort will call qSort will call...
This line was written for optimization.
If there is no data in array you have nothing to sort.
Plus when you sort elements you are passing smaller and smaller chunks of base array to qSort function, so you have to finish qSort execution at one point.
So basically it tells the code "There is nothing to sort".

How to return the 3 most and 3 less frequent numbers out of a list, elegantly

I want to use underscore for this, but for some silly reason it escapes me:
I have a list of numbers:
39, 39, 10, 1, 3, 4, 5... etc
I want to return the 3 most frequent and the 3 less frequent.
I've tried using _.countBy but that somehow returns an object, which makes it hard (?) to sort, unless perhaps I'm missing the obvious.
Call me crazy, but here is a solution without underscore, it's not particularly fast since I sort which takes O(n log n) instead of searching for the maximal three which is O(n) but if it really matters I can fix it.
Let our array be:
var arr = [1,1,1,1,1,1,2,3,4,5,6,7,7,8,9,10,9,8,9,8]
First, we reduce the numbers to their frequencies:
var t =arr.reduce(function(a,b){
a[b] = (b in a) ? a[b]+1 : 1; // set to 1 if not there, else increase
return a; // return the object
},{});
var res = Object.keys(t).sort(function(x,y){ // sort by frequency
return t[x] > t[y];
});
// Res more generally contains the frequencies ordered
alert(res[0]+" "+res[1]+" "+res[2]);
Here is a fiddle.
Ok, so I lied - I'm a sucker for native JavaScript, here is the "easier" Underscore version:
obj = _.countBy(arr,function(num){return num; }); // get array by frequencies
var res = _.keys(obj).sort(function(x,y){ return obj[x] - obj[y]});
//res is now the same as above, to be honest I like the native version better :)
Underscore fiddle if that's your thing.
First this groups them without parameters (just creates new arrays with each having the same value), and then sorts them by the resulting array lengths. Then creates two new arrays of min and max, using the first three and last three elements. .map is used to return only the first element instead of the array created by .groupBy.
EDIT: This is Benjamin Gruenbaum's slicker edit of my original answer.
var arr = [1,1,1,1,1,1,2,3,4,5,6,7,7,8,9,10,9,8,9,8];
var group = _.sortBy(_.groupBy(arr), "length");
var min = _.pluck(group.slice(0, 3),0);
var max = _.pluck(group.slice(-3),0);
console.log(min);
console.log(max);
jsFiddle

Categories