Why won't my function work when I use splice? - javascript

I am trying to write a function which should calculate all prime numbers up to an input parameter and return it. I am doing this for practice.
I wrote this function in a few ways but I was trying to find new ways to do this for more practice and better performance. The last thing I tried was the code below:
function primes(num){
let s = []; // sieve
for(let i = 2; i <= num; i++){
s.push(i);
}
for(let i = 0; i < s.length; i++) {
for(let j = s[i]*s[i]; j <= num;) {
//console.log(j);
if(s.indexOf(j)!= -1){
s.splice(s.indexOf(j), 1, 0);
}
j+=s[i];
}
}
s = s.filter(a => a != 0);
return s;
}
console.log(primes(10));
The problem is that when I run this in a browser it keeps calculating and won't stop and I don't know why.
Note: when I comment out the splice and uncomment console.log(j); everything works as expected and logs are the things they should be but with splice, the browser keep calculating and won't stop.
I am using the latest version of Chrome but I don't think that can have anything to do with the problem.

Your problem lies in this line:
s.splice(s.indexOf(j), 1, 0);
Splice function third argument contains elements to be added in place of the removed elements. Which means that instead of removing elements, you are swapping their values with 0's, which then freezes your j-loop.
To fix it, simply omit third parameter.
function primes(num){
let s = []; // sieve
for(let i = 2; i <= num; i++){
s.push(i);
}
for(let i = 0; i < s.length; i++) {
for(let j = s[i]*s[i]; j <= num;) {
//console.log(j);
if(s.indexOf(j)!= -1){
s.splice(s.indexOf(j), 1);
}
j+=s[i];
}
}
return s;
}
console.log(primes(10));

Your problem is in this loop:
for(let j = s[i]*s[i]; j <= num;)
This for loop is looping forever because j is always less than or equal to num in whatever case you're testing. It is very difficult to determine exactly when this code will start looping infinitely because you are modifying the list as you loop.
In effect though, the splice command will be called setting some portion of the indexes in s to 0 which means that j+=s[i] will no longer get you out of the loop.

Related

Is there a faster way to find a value in a multidimensional array and return all of its indexes than for(for())?

I need to find the value of a 2-dimensional array and return its indexes. For example if my searched term is in array[i][j], then I want [i, j] returned.
Naturally, I came up with this simple solution:
function find(str){
for(let o = 0; o < array.length; o++){
for(let k = 0; k < array[o].length; k++){
if(array[o][k] == str){
return [o, k];
}
}
}
}
Now I need to use this method a couple hundred times as part of an algorithm, and it gets quite time-costy. Is there a more efficient way?
I have created a simple full example including a 'benchmark':
// setup to hide foo in an array
var array = [];
for(let i = 0; i < 100; i++){
array.push([])
for(let j = 0; j < 100; j++){
if(i == 99 && j == 99) array[i].push("foo"); // intentionally hiding the searched term at the worst-case position for the algorithm of find()
else array[i].push("bar");
}
}
// function to find foo
function find(str){
for(let o = 0; o < array.length; o++){
for(let k = 0; k < array[o].length; k++){
if(array[o][k] == str){
return [o, k];
}
}
}
}
// lets say we need to find foo 200 times
var a = window.performance.now();
for(let i = 0; i < 200; i++){
console.log(i, find("foo")); // if you're happy and you know it, tell us what you found
}
var b = window.performance.now();
// print performance result
$('body').html((b-a) + " ms");
JSfiddle for the benchmark example: http://jsfiddle.net/3t0db1cq/11/
(note: in that benchmark example I searched 'foo' 200 times, so you may ask why I don't simply cache it. In reality I will search different terms, so caching will barely improve the performance. Also I intentionally did put the searched term in the worst-case position of the array for this benchmark)
Can you help me find a better algorithm for find()? To be fair for the performance test, re-position the searched term at the worst-case position in the array for your algorithm, if you want to compare the results.
(Target for this is websites, so all common browsers should support it)
Seems to me that you are mapping a key (pair of integers) to a string value and you want to return the key for that value.
As you are using an array each search operation is always O(n^2) worse case, there's no "smart" way using that datastructure
As #Richrd said, you can build a reverse mapping from the string values to a pair of integers and search that. Easy way is to use a javascript Map() (hash map). Though you may want to look into a trie implementation for the string to integer map.
But this begs the question: if you are performing a lot of these reverse lookups then why store this data as a 2d-array of strings in the first place? You could save more time by storing this data as a map of strings to ints in the first place.
If you are familiar with SQL one way of doing this is to use sqllite. It is a very easy way to run sql in the browser.
https://www.tutorialspoint.com/html5/html5_web_sql.htm
Unfortunately it is not supported on all browsers so you would have to have a general idea of your audience.
Alternatively as long as all of your values are different you could reverse map your array and then search as much as you want with no cost. For instance:
// setup to hide foo in an array
var array = [];
for(let i = 0; i < 100; i++){
array.push([])
for(let j = 0; j < 100; j++){
if(i == 99 && j == 99) array[i].push("foo"); // intentionally hiding the searched term at the worst-case position for the algorithm of find()
else array[i].push("bar");
}
}
//Create your reverse mapped array. This only runs once at startup, but now allows you to
function buildReverse(arr) {
var reverseArr = {};
for(let o = 0; o < arr.length; o++){
for(let k = 0; k < arr[o].length; k++){
reverseArr[arr[o][k]] = [o, k];
}
}
return reverseArr
}
var reverseArr = buildReverse(array);
function find(str){
if (reverseArr[str] != undefined) {
return reverseArr[str];
// or
//return [reverseArr[str][0], reverseArr[str][1]]
//, etc...
}
return "";
}
// lets say we need to find foo 200 times
var a = window.performance.now();
for(let i = 0; i < 200; i++){
console.log(i, find("foo")); // if you're happy and you know it, tell us what you found
}
var b = window.performance.now();
// print performance result
$('body').html((b-a) + " ms");

How come it doesn't slice a number until the end despite including the number's length

I made a code to extract every odd numbers from one number, and it works for numbers that are not too long such as "1341" (which give me the numbers "1,13,1341,341,41,1") but oddly doesn't work for very long numbers.
function solve(s) {
var newarray = [];
for (var i = 0; i <= s.length; i++) {
for (var j = 0; j <= s.length; j++) {
var slicing = s.slice(i, j);
if (slicing % 2 !== 0) {
newarray.push(slicing);
}
}
}
return newarray.length;
}
Despite putting s.length, it slices until a certain point. For example:
With "93711892377292643581488317", it slices until "9371189237729", then when it starts from 3 it slices until "93711892377292643" (until the next odd number)
With "65266112954758467", from the start it slices until "6526611295475", then when it starts from 5, it slices until "65266112954758467" (until the next odd number).
What's going on?
slicing % 2 doesn't work properly when slicing is large. Javascript treats large numbers as floating-point numbers, which means it's not accurate to know the value to the nearest integer - in binary, the units bit becomes 0, so it's a multiple of 2.
You want to count all odd numeric substrings within a numeric string.
First, consult the documentation of str.slice(beginIndex[, endIndex]).
Then, in order to gain a better understanding of your code, it is helpful to slowly iterate through a few steps of your loops and write down the expected vs. the observed output.
I recommend to use the debugger built into all modern browsers:
Add a debugger; statement into the inner for-loop:
function solve(s) {
var newarray = [];
for (var i = 0; i <= s.length; i++) {
for (var j = 0; j <= s.length; j++) {
var slicing = s.slice(i, j);
debugger; // <-- we want to break here and check our values
if (slicing % 2 !== 0) {
newarray.push(slicing);
}
}
}
return newarray.length;
}
Press [F12] and run this code in your browser's console for some exemplary input.
The debugger tab should now pop up. Press [F8] to step through your code and keep track of the value of your slicing variable.
You will probably notice that slicing is empty at the beginning. You should start your inner loop from j = i + 1 to fix that.
Also, you might notice that your i iterates one time too many, so that slicing is empty during the final iterations of the inner for-loop. You need to terminate your outer loop one step earlier.
Then, for the problematic input "93711892377292643581488317" you will notice that large numeric slices such as "93711892377292643" will not be recognized as odd. "93711892377292643" % 2 evaluates to 0 instead of 1. In order to understand this, you need to know that JavaScript numbers are internally represented as limited precision floating point values. If you put 93711892377292643 into your browser console, it will evaluate to 93711892377292640 - an even number! JavaScript can only handle integer numbers up to Number.MAX_SAFE_INTEGER == 9007199254740991 without introducing such truncation errors.
Now, how to solve this issue? Well, a number is odd if and only if the last digit is odd. So we don't have to inspect the full number, just the last digit:
function solve(s) {
var newarray = [];
for (var i = 0; i < s.length; i++) {
for (var j = i; j < s.length; j++) {
var digit = s.slice(j, j + 1);
if (digit % 2 !== 0) {
var slicing = s.slice(i, j + 1);
newarray.push(slicing);
}
}
}
return newarray.length;
}
console.log(solve("1234567890")); // 25
Once you have sufficient understanding of this code, you could start improving it. You could for example replace the newarray with a simple counter, as you are only interested in the number of off digits, not the digits themselves.
A faster solution could be written down as follows:
function solve(str) {
let count = 0;
for (let i = 0; i < str.length; i++) {
if (str[i] % 2) count += i + 1;
}
return count;
}
console.log(solve("1234567890")); // 25
Or, written in a more declarative way:
const solve = (str) =>
str.split('').reduce((count, chr, i) => chr % 2 ? count + i + 1 : count, 0);
console.log(solve("1234567890")); // 25

Understanding get second lowest and second highest value in array

Good day fellow Stack-ers,
I must ask your pardon if this question has been asked before or if it seems elementary (I am only a Javascript novice).
I have been doing w3c js challenges lately: Write a JavaScript function which will take an array of numbers stored and find the second lowest and second greatest numbers.
Here is my answer:
var array = [3,8,5,6,5,7,1,9];
var outputArray = [];
function arrayTrim() {
var sortedArray = array.sort();
outputArray.push(sortedArray[1],array[array.length-2]);
return outputArray;
}
arrayTrim();
and here is the answer that they have provided:
function Second_Greatest_Lowest(arr_num) {
arr_num.sort(function(x,y) {
return x-y;
});
var uniqa = [arr_num[0]];
var result = [];
for(var j=1; j < arr_num.length; j++) {
if(arr_num[j-1] !== arr_num[j]) {
uniqa.push(arr_num[j]);
}
}
result.push(uniqa[1],uniqa[uniqa.length-2]);
return result.join(',');
}
alert(Second_Greatest_Lowest([1,2,3,4,5]));
I know that the for loop runs through until the length of the input, but I don't understand the if statement nested within the for loop. It seems like a long way around to the solution.
Thank you!
Your answer does not perform correct for input such as f.e. [3,8,5,6,5,7,1,1,9]. Your proposed solution returns 1 as the second lowest number here – whereas it should actually be 3.
The solution suggested by the site takes that into account – that is what the if inside the loop is for, it checks if the current number is the same as the previous one. If that’s the case, it gets ignored. That way, every number will occur once, and that in turn allows to blindly pick the second element out of that sorted array and actually have it be the second lowest number.
It seems like a long way around to the solution
You took a short cut, that does not handle all edge cases correctly ;-)
The loop in question:
for(var j=1; j < arr_num.length; j++) {
if(arr_num[j-1] !== arr_num[j]) {
uniqa.push(arr_num[j]);
}
}
Provides some clue as to what it's doing by using a (reasonably) descriptive variable name: uniqa - or "unique array". The if statement is checking that the current element is not the same as the previous element - having sorted the array initially this works to give you a unique array - by only filling a new array if the element is indeed unique.
Thereafter the logic is the same as yours.
import java.util.Arrays;
public class BubbleWithMax_N_Min
{
public static void main(String[] agrs)
{
int temp;
int[] array = new int[5];
array[0] = 3;
array[1] = 99;
array[2] = 55;
array[3] = 2;
array[4] = 1;
System.out.println("the given array is:" + Arrays.toString(array));
for (int i = 0; i < array.length; i++)
{
System.out.println(array[i] + "");
}
for (int i = 0; i < array.length; i++)
{
for (int j = 1; j < array.length - i; j++)
{
if (array[j - 1] > array[j])
{
temp = array[j - 1];
array[j - 1] = array[j];
array[j] = temp;
}
}
}
System.out.println(" 2nd Min and 2nd Highest:");
for (int i = 0; i < 1; i++)
{
System.out.println(array[i+1]);
}
for (int i = 0; i < 1; i++)
{
int a= array.length-2;
System.out.println(array[a]);
}
}
}

Removing Duplicate Items in an Array without Regex or filter

I have been stumped on this problem for a few hours now and am making no progress. I feel like this should be simple. I am trying to Remove duplicate characters in a String without using methods such as Filter or a Reg ex.
Here is my current code:
var duplicate = function(string) {
var newString = string.split("");
var finalArrayWithNoDuplicates = []
for (var i = 0; i < newString.length; i++){
for (var=0; j < newString.length; i++){
while(newString[i])
if (newString[i] !== newString[j]){
}
}
}
return finalArrayWithNoDuplicates.join("");
};
I am able to filter one letter at a time but as I progress down the chain in the while statement I am adding letters that were filtered out originally.
All of the algorithm tutorials for this algorithm are in Java that I have been finding. Is there a way to do this with only using a a for and while loops?
There are several things wrong with the proposed code:
It has serious errors (the inner loop is written all wrong)
You don't need to involve arrays at all, strings will do just fine
The "if char !== other char" check will never provide enough information to act on
Here's an alternative version using for loops and the same basic idea:
function deduplicate(str) {
var result = "";
for (var i = 0; i < str.length; ++i) {
var found = false;
for (var j = 0; j < i; ++j) {
if (str[i] == str[j]) {
found = true;
break;
}
}
if (!found) result += str[i];
}
return result;
}
Each character str[i] in the input string is compared to all characters str[j] that precede it (there is no point in comparing to characters that follow it because we are going to process those when their turn comes up anyway). If the character is not equal to any of those that precede it then we know it's the first of its kind to appear and include it in the result.
Note that this algorithm has O(n²) performance, which is very poor compared to other possible approaches. Its main selling point is that it is straightforward and that everything happens "in front of your eyes".
Here is a slightly modified version of your function that uses an object to keep track of which letters have already been encountered:
var duplicate = function(string) {
var finalArrayWithNoDuplicates = [];
var seen = {};
for (var i = 0; i < string.length; i++) {
if (!seen[string[i]]) {
finalArrayWithNoDuplicates.push(string[i]);
seen[string[i]] = 1;
}
}
return finalArrayWithNoDuplicates.join("");
};
No need for two nested for-loops
No need for "while" loop as well
in the following line of code there are two errors: for (var=0; j < newString.length; i++){ first one is var=0 (compilation error) and second is theat you increment i instead of j
It can be done by adding only unique elements (that don't appear twice) to finalArrayWithNoDuplicates
as follows:
var duplicate = function(newString) {
var finalArrayWithNoDuplicates = []
var x = 0;
for (var i = 0; i < newString.length; i++){
// if the char appears in another index
// or if it's already in the result - don't add it
if (newString.lastIndexOf(newString[i]) !== i || finalArrayWithNoDuplicates.indexOf(newString[i]) > -1){
continue;
}
else{
finalArrayWithNoDuplicates[x++] = newString[i];
}
}
return finalArrayWithNoDuplicates.join("");
};
var arr = [1,2,3,4,5,4,5,6,7];
alert(duplicate(arr));
OUTPUT:
1234567

Comparing array to var during iteration(cont.)

So here's my problem.. Might just be tired but, I want counter to ++ only if number has not occurred in array.
Meaning during the 4 iterations, counter should ++ only on iteration 1,3,4
var array = [], number, temp = [4,2,5,9], counter = 0;
for(var i = 0; i <= 3; i += 1) {
array.push(i);
number = temp[i];
}
document.write(counter);
But I'm drawing a blank here... Help?
(No this isn't homework)
if (array.indexOf(number) < 0)
counter++;
unfortunately JS doesn't have an "in_array", but it's pretty straight forward:
#MikeSamuel pointed out you can use indexOf (thanks Mike). So with that being said:
var array = [], number, temp = [4,2,5,9], counter = 0;
for(var i = 0; i <= 3; i += 1) {
array.push(i);
number = temp[i];
if (temp.indexOf(i)==-1) // much simpler, assuming you're checking if i is in temp.
counter++; // increase counter if it is not.
}
document.write(counter);
I'm not sure where you want the logic, so you'll have to figure that out or be more specific. Just know that you need to iterate through the array you're checking and check if the "needle" is in the "haystack" array.
EDIT Had the opposite, just added bool to check for existence.

Categories