Marking "visited" to javascript array elements presenting contradictory results - javascript

JS guys/gals! I had the impression that when using BFS in 2D arrays in JavaScript, we can directly mark "visited" on array elements like this: grid[i][j].visited = true, and then next time when we check grid[i][j].visited, it will return true. Here's a piece of sample code that works this way (feel free to run it):
var findP = function(origin, grid) {
var q = [origin], count = 0;
grid[origin[0]][origin[1]].visited = true;
var arr = [[0, -1], [0, 1], [-1, 0], [1, 0]];
while(q.length > 0) {
var c = q.length;
count++;
while(c >= 0) {
var t = q.shift();
if (grid[t[0]][t[1]] === '#') {
return count;
}
for(var i = 0; i < arr.length; i++) {
var m = t[0] + arr[i][0];
if (m < 0 || m > grid.length - 1) {
continue;
}
var n = t[1] + arr[i][1];
if (n < 0 || n > grid[0].length - 1) {
continue;
}
if (grid[m][n] !== 'X' && !grid[m][n].visited) {
grid[m][n].visited = true;
q.push([m, n]);
}
}
c--;
}
}
return -1;
}
console.log(findP([1, 1],[['X','X','X','X','X'], ['X','*','X','O','X'],
['X','O','O','#','#'],['X','X','X','X','X']]))
The above code works fine. However, when I tried this, it doesn't work at all (infinite loop alert. won't be fun runnig it):
var shortestPath = function(arr) {
if (!arr || arr.length === 0) {
return -1;
}
var i, j, hasBen = false;
for (i = 0; i < arr.length; i++) {
for (j = 0; j < arr[i].length; j++) {
if (arr[i][j] === '*') {
hasBen = true;
break;
}
}
if (hasBen) {
break;
}
}
//(i, j) is where Ben is:
var steps = 0, helper = [[1, 0], [-1, 0], [0, 1], [0, -1]];
var q = [[i, j]], flag = false;
arr[i][j].visited = true;
console.log(arr[i][j].visited); // undefined
console.log(arr[0][0].visited); //undefined
while(q.length > 0) {
var counter = q.length;
steps++;
while(counter > 0) {
var temp = q.shift(); // temp = [i, j];
for (var x = 0; x < helper.length; x++) {
var t = helper[x];
var m = temp[0] + t[0];
var n = temp[1] + t[1];
if (m < 0 || m >= arr[0].length || n < 0 || n >= arr.length) {
continue;
}
if (!arr[m][n].visited) {
arr[m][n].visited = true;
if (arr[m][n] === '#') {
flag = true;
break;
}
if (arr[m][n] !== 'X') {
q.push([m, n]);
}
}
}
if (flag) {
break;
}
counter--;
}
}
return flag ? steps : -1;
}
shortestPath([['*', 'O', 'O', 'X'], ['X', 'X', 'O', 'X'], ['X', 'X', '#', 'X']]);
The two console.log() in the above code both logs undefined, which leads to infinite loop. However, why the first code snippet works yet the second one doesn't? Aren't they the same when attaching .visited on array elements? I am so confused.

Related

JavaScript Elements of array being equal function not working

While taking some online JS tests, I am stuck on why my function for testing if the elements within two arrays are equal is not working. I am using the function to make sure that the new array does not have duplicate values.
The following is my function for testing if the elements of the arrays are equal or not:
const isEqualArr = (a, b) => {
for (let i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
};
I call to the function within a for loop, then I test for undefined, because the first time around, the outputArr is empty
for (let x = 0; x < numsLength; x++) {
if (typeof outputArr[x] != 'undefined') {
if (isEqualArr(tempArr, [outputArr[x]])) {
However, the result shows that there are duplicates:
0: Array [ -1, 0, 1 ]
​
1: Array [ -1, 2, -1 ]
​
2: Array []
​
3: Array [ -1, -1, 2 ]
​
4: Array []
​
5: Array [ 0, 1, -1 ]
​
6: Array [ 0, -1, 1 ]
​
7: Array [ 1, 0, -1 ]
​
8: Array [ -1, 0, 1 ]
The other issue is that not only do I have two null arrays (duplicates), but when I test for null before the push, it is not working either:
if(tempArr != null ) {
outputArr.push(tempArr);
}
I have tried tempArr[0] != null, tempArr[0] != '' as well as
if (typeof tempArr[0] != 'undefined')
But it still inserts null arrays into the outputArr.
The following is the premise of the test:
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
Notice that the solution set must not contain duplicate triplets. Given nums = [-1,0,1,2,-1,-4]
The following code is my solution, but the duplicate entries, as well as the null entries, makes this an incorrect solution. If you guys can see why my isEqualArr function is at fault, then I would appreciate it.
const threeSum = (nums) => {
let numsLength = nums.length;
let tempArr = [];
let outputArr = [];
if (numsLength > 3) {
for (let i = 0; i < numsLength; i++) {
for (let j = 1; j < numsLength; j++) {
for (let k = 2; k < numsLength; k++) {
if (
i != j &&
i != k &&
j != k &&
nums[i] + nums[j] + nums[k] == 0
) {
tempArr[0] = nums[i];
tempArr[1] = nums[j];
tempArr[2] = nums[k];
for (let x = 0; x < numsLength; x++) {
if (typeof outputArr[x] != 'undefined') {
if (!isEqualArr(tempArr, [outputArr[x]])) {
if (typeof tempArr[0] != 'undefined') {
outputArr.push(tempArr);
console.log(
'temp: ' + tempArr + ' outputArr: ' + outputArr[x]
);
console.log(
'compare: ' + isEqualArr(tempArr, outputArr[x])
);
console.log(outputArr);
tempArr = [];
}
}
}
}
if (i == 0) {
outputArr.push(tempArr);
tempArr = [];
} else {
// do nothing
}
}
}
}
}
}
return outputArr;
};
const isEqualArr = (a, b) => {
for (let i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
};
EDIT: I was able to eliminate the null entries by moving the code that checks if it is the first time to run above the for loop:
if (i == 0) {
outputArr.push(tempArr);
tempArr = [];
} else {
// do nothing
}
for (let x = 0; x < numsLength; x++) {
if (typeof outputArr[x] != 'undefined') {
if (!isEqualArr(tempArr, [outputArr[x]])) {
if (typeof tempArr[0] != 'undefined') {
outputArr.push(tempArr);
console.log(
'temp: ' + tempArr + ' outputArr: ' + outputArr[x]
);
console.log(
'compare: ' + isEqualArr(tempArr, outputArr[x])
);
console.log(outputArr);
tempArr = [];
}
}
}
}
BTW, another thing that is not working if the following logic:
i != j &&
i != k &&
j != k &&
You can see by the outputArr that this logic is being completely ignored.
REFACTORED: even though the online test marked my solution as wrong, you can see with the results, that it is correct. Especially when you factor in the conditionals for making sure that nums[i] != nums[j] and so forth:
const threeSum = (nums) => {
let numsLength = nums.length;
let tempArr = [];
let outputArr = [];
for (let i = 0; i < numsLength; i++) {
for (let j = 0; j < numsLength; j++) {
for (let k = 0; k < numsLength; k++) {
if (
nums[i] != nums[j] &&
nums[i] != nums[k] &&
nums[j] != nums[k] &&
nums[i] + nums[j] + nums[k] == 0
) {
if (typeof outputArr[0] != 'undefined') {
/**********
* if it reaches this conditional
* then outputArr has at least one element
***********/
if (typeof outputArr[k] != 'undefined') {
tempArr[0] = nums[i];
tempArr[1] = nums[j];
tempArr[2] = nums[k];
if (isEqualArr(tempArr, outputArr)) {
// do nothing because there is already an element within the array
} else {
outputArr.push(tempArr);
// empty tempArr after insert
tempArr = [];
}
}
} else {
// push triplet elements into temp array for the first iteration only
tempArr[0] = nums[i];
tempArr[1] = nums[j];
tempArr[2] = nums[k];
// insert tempArr for the first iteration only
outputArr.push(tempArr);
// empty tempArr after insert
tempArr = [];
}
}
}
}
}
return outputArr;
};
const isEqualArr = (a, b) => {
for (elem in b) {
for (let i = 0; i < a.length; i++) {
if (a[i] != elem[i]) {
// do nothing
} else {
return true;
}
}
}
return false;
};
RESULTS:
(3) [Array(3), Array(3), Array(3)]
0: (3) [-1, 0, 1]
1: (3) [1, 0, -1]
2: (3) [-1, 1, 0]
length: 3
Your inner for loop is confusing, if you replace it by a forEach you can see what's going on, you should pass as argument outputArr[x] not [outputArr[x]] and there's more stuff.
const threeSum = (nums) => {
let numsLength = nums.length;
let tempArr = [];
let outputArr = [];
if(numsLength < 3) return;
for (let i = 0; i < numsLength; i++) {
for (let j = 1; j < numsLength; j++) {
for (let k = 2; k < numsLength; k++) {
if (
i != j &&
i != k &&
j != k &&
nums[i] + nums[j] + nums[k] == 0
) {
tempArr.push(nums[i], nums[j], nums[k]); // push three at the same time, it's cleaner and avoid problems
outputArr.forEach((arr, index) => {
if(!isEqualArr(arr, outputArr[index])) outputArr.push(tempArr); // outputs correctly
});
if (i == 0) {
outputArr.push(tempArr);
tempArr = [];
}
}
}
}
}
return outputArr;
};
const isEqualArr = (a, b) => {
for (let i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
};
console.log(threeSum([-1,0,1,2,-1,-4]));

Struggling with while loop in this algorithm

I am writing a program to calculate euclidean distance and then display the lines based, with the below code:
function discreteFrechet(X, Y) {
var M = X.length;
var N = Y.length;
var S = [
[],
[]
];
var backpointers = [
[],
[]
];
var backpaths = [];
var idx;
var path = [];
var paths;
var back = [
[],
[]
];
for (i = 0; i < M; i++) {
for (j = 0; j < N; j++) {
S[i][j] = 0;
backpointers[i][j] = 0;
}
} /* populates S array */
/*sanity check*/
S[0, 0] = euclidian(X, Y, 0, 0);
opt1 = [-1, 0];
opt2 = [0, -1];
opt3 = [-1, -1];
backpaths.push(opt1);
backpaths.push(opt2);
backpaths.push(opt3);
/*backpaths populated*/
for (i = 0; i < M; i++) {
for (j = 0; j < N; j++) {
options = [];
if (i != 0 || j != 0) {
if (i > 0) {
options[0] = S[i - 1, j];
}
if (j > 0) {
options[1] = S[i - 1, j];
}
if (i > 0 && j > 0) {
options[2] = S[i - 1, j];
}
idx = Math.min(options);
backpointers[i][j] = idx;
S[i][j] = Math.max(options[idx], euclidian(X, Y, i, j));
}
}
}
console.log(S);
paths = [
[M - 1, N - 1]
];
path = [
[],
[]
];
path.push(paths);
//Create "path"
i = M - 1;
j = N - 1;
count = 0;
while ((path[path.length - 1][0] != 0) || (path[0][path[1].length - 1] != 0)) {
back[0][1] = backpaths[backpointers[i], [j]];
i += back[0];
j += back[1];
path.push([i, j]);
if (count > 1000) {
console.log("too many loops");
break;
}
count += 1;
}
path.push([0, 0]);
path.reverse();
//returns bottleneck and the path
}
As I am testing, I am running into a problem with an infinite while loop (hence the break statement) any help or suggestions would be greatly appreciated! The goal is to append indicies into the path element, such that I can then take those path indicies and the bottleneck and use them to plot with d3.

Print missing elements that lie in range 0 – 99

I wrote code in JavaScript but issues with start value ex: ?-9, ?-87...etc
Input: {88, 105, 3, 2, 200, 0, 10}
<script type="text/javascript">
function RangeValues(){
var a = [88, 105, 3, 2, 200, 0, 10];
var n = a.length;
var pq = [];
for(var i = 0; i<n; i++) {
if(a[i] >= 0 && a[i] <= 99)
pq.push(a[i]);
}
var start = 0;
if(!Array.prototype.last) {
Array.prototype.last = function() {
return this[this.length - 1];
}
}
while(pq.length != 0)
{
var num = pq.last();
pq.pop();
if(num - start > 1){
console.log( (start) +','+ (num-1));
}
if((num - start) == 1)
{
start = num + 1;
console.log(start);
}
}
if(start == 99){
console.log('99');
}
else if(start < 100) {
console.log(start+' to '+99);
}
return 0;
}
RangeValues();
</script>
actual output: 0-9,0-1,0-2,0-87,0-99
Expected: 1,4-9,11-87,89-99
First, you should sort number in array pq descending so the last value is the min number. Try with below solution:
function RangeValues(){
var a = [88, 105, 3, 2, 200, 0, 10];
//var a = [12, 105, 23, 1, 200, 4, 21];
var n = a.length;
var pq = [];
for(var i = 0; i<n; i++) {
if(a[i] >= 0 && a[i] <= 99){
pq.push(a[i]);
}
}
// sort array descending
pq.sort(function(a, b){return b-a});
var start = 0;
if(!Array.prototype.last) {
Array.prototype.last = function() {
return this[this.length - 1];
}
}
while(pq.length != 0){
var num = pq.last();
pq.pop();
if (num-start == 2 || num == 1){
console.log(num-1);
} else if((num - start)>2){
var from = start + 1;
var end = num - 1;
console.log(from+" - "+end);
}
start = num;
}
if (start < 98){
var from = start + 1;
console.log(from+" - "+99);
} else if( start == 98){
console.log(start+1);
}
return 0;
}
RangeValues();

Multiple array intersection in javascript

How to write in javascript(w/wth JQuery) to find values that intersect between arrays?
It should be like
var a = [1,2,3]
var b = [2,4,5]
var c = [2,3,6]
and the intersect function should returns array with value {2}. If possible it could applicable for any number of arrays.
Thanks
There are many ways to achieve this.
Since you are using jQuery I will suggest use grep function to filter the value that are present in all three array.
var a = [1, 2, 3]
var b = [2, 4, 5]
var c = [2, 3, 6]
var result = $.grep(a, function(value, index) {
return b.indexOf(value) > -1 && c.indexOf(value) > -1;
})
console.log(result)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Explanation: Loop over any array and filter out the values that are present in other array.
Update (for multimidensional array):
Concept - flatten the multidimensional array that is transform [[1,2],3,4] to [1,2,3,4] and then use the same logic used for single dimensional array.
Example:
var a = [
[1, 4], 2, 3
]
var b = [2, 4, 5]
var c = [2, 3, 6, [4, 7]]
//flatten the array's
//[1,4,2,3]
var aFlattened = $.map(a, function(n) {
return n;
})
//[2,4,5]
var bFlattened = $.map(b, function(n) {
return n;
})
//[2,3,6,4,7]
var cFlattened = $.map(c, function(n) {
return n;
})
var result = $.grep(aFlattened, function(value) {
return (bFlattened.indexOf(value) > -1 && cFlattened.indexOf(value) > -1);
});
console.log(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
// First this is how you declare an array
var a = [1,2,3];
var b = [2,4,5];
var c = [2,3,6];
// Second, this function should handle undetermined number of parameters (so arguments should be used)
function intersect(){
var args = arguments;
// if no array is passed then return empty array
if(args.length == 0) return [];
// for optimisation lets find the smallest array
var imin = 0;
for(var i = 1; i < args.length; i++)
if(args[i].length < args[imin].length) imin = i;
var smallest = Array.prototype.splice.call(args, imin, 1)[0];
return smallest.reduce(function(a, e){
for(var i = 0; i < args.length; i++)
if(args[i].indexOf(e) == -1) return a;
a.push(e);
return a;
}, []);
}
console.log(intersect(a, b, c));
First of all '{}' means Object in JavaScript.
Here is my suggestion.(this is another way of doing it)
// declarations
var a = [1,2,3];
var b = [2,4,5];
var c = [2,3,6];
// filter property of array
a.filter(function(val) {
if (b.indexOf(val) > -1 && c.indexOf(val) > -1)
return val;
});
what it does is it checks for each element in array 'a' and checks if that value is present in array 'b' and array 'c'. If it is true it returns the value. Simple!!!. The above code should work for String as well but it wouldn't work for IE < 9, so be careful.
// Intersecting 2 ordered lists of length n and m is O(n+m)
// This can be sped up by skipping elements
// The stepsize is determined by the ratio of lengths of the lists
// The skipped elements need to be checked after skipping some elements:
// In the case of step size 2 : Check the previous element
// In case step size>2 : Binary search the previously skipped range
// This results in the best case complexity of O(n+n), if n<m
// or the more propable complexity of O(n+n+n*log2(m/n)), if n<m
function binarySearch(array, value, start = 0, end = array.length) {
var j = start,
length = end;
while (j < length) {
var i = (length + j - 1) >> 1; // move the pointer to
if (value > array[i])
j = i + 1;
else if (value < array[i])
length = i;
else
return i;
}
return -1;
}
function intersect2OrderedSets(a, b) {
var j = 0;
var k = 0;
var ratio = ~~(b.length / a.length) - 1 || 1;
var result = [];
var index;
switch (ratio) {
case 1:
while (j < a.length) {
if (a[j] === b[k]) {
result.push(a[j]);
j++;
k++;
} else if (a[j] < b[k]) {
while (a[j] < b[k]) j++;
} else {
while (b[k] < a[j]) k++;
if (k >= b.length) break;
}
}
break;
case 2:
while (j < a.length) {
if (a[j] === b[k]) {
result.push(a[j]);
j++;
k++;
} else if (a[j] < b[k]) {
while (a[j] < b[k]) j++;
} else {
while (b[k] < a[j]) k += 2;
if (k - 1 >= b.length) break;
if (a[j] <= b[k - 1]) k--;
}
}
break;
default:
while (j < a.length) {
if (a[j] === b[k]) {
result.push(a[j]);
j++;
k++;
} else if (a[j] < b[k]) {
while (a[j] < b[k]) j++;
} else {
while (b[k] < a[j]) k += ratio;
index = binarySearch(b, a[j], k - ratio + 1, k + 1 < b.length ? k + 1 : b.length - 1);
if (index > -1) {
result.push(a[j]);
j++;
k = index + 1;
} else {
j++;
k = k - ratio + 1;
}
if (k >= b.length) break;
}
}
}
return result;
}
function intersectOrderedSets() {
var shortest = 0;
for (var i = 1; i < arguments.length; i++)
if (arguments[i].length < arguments[shortest].length) shortest = i;
var result = arguments[shortest];
for (var i = 0, a, b, j, k, ratio, index; i < arguments.length; i++) {
if (result.length === 0) return result;
if (i === shortest) continue;
a = result;
b = arguments[i];
result = intersect2OrderedSets(a, b);
}
return result;
}
How to use:
intersectOrderedSets(a,b,c);

Get smallest and highest number to the right and left to my value

Please let me know if there is a faster, more elegant way to get this result:
I have an array of numbers, and once set a value i should get smallest and highest number to the right and left to my value.
For example if I have: [1,2,3,4,6,7,8,9] and a value is 5,
my numbers will be:1, 4,6,9
if value is 4
my numbers will be 1,4,4,9
My horrible code is:
var arr = [1, 8, 2, 3, 9, 5, 4, 6, 7];
var result1 = [];
var result2 = [];
var goal = 5;
for (a = 0; a < arr.length; a++) {
if (arr[a] < goal) {
result1.push(arr[a])
} else if (arr[a] === goal) {
result1.push(arr[a]);
result2.push(arr[a]);
} else {
result2.push(arr[a]);
}
};
var count1 = result1[0];
for (x = 0; x < result1.length; x++) {
if (result1[x] < count1) {
count1 = result1[x]
}
};
var count11 = result1[0];
for (xx = 0; xx < result1.length; xx++) {
if (result1[xx] > count11) {
count11 = result1[xx]
}
};
var count2 = result2[0];
for (y = 0; y < result2.length; y++) {
if (result2[y] > count2) {
count2 = result2[y]
}
};
var count22 = result2[0];
for (yy = 0; yy < result2.length; yy++) {
if (result2[yy] < count22) {
count22 = result2[yy]
}
};
console.log(count1 + ' ' + count11 + ' ' + count22 + ' ' + count2)
You can simplify your code a lot by using a few mighty methods: Array::filter and Math.min/max:
var arr = [1, 8, 2, 3, 9, 5, 4, 6, 7];
var goal = 5;
var result1 = arr.filter(function(x) { return x <= goal });
var result2 = arr.filter(function(x) { return x >= goal });
var count1 = Math.min.apply(Math, result1);
var count11 = Math.max.apply(Math, result1);
var count2 = Math.min.apply(Math, result2);
var count22 = Math.max.apply(Math, result2);
Assuming the array is sorted always,
var array = [1, 2, 3, 4, 6, 7, 8, 9],
number = 5,
pivot, small, large;
for (var i = 0, len = array.length; i < len; i += 1) {
if (array[i] >= number) {
pivot = i;
break;
}
}
if (pivot > 0) {
small = [array[0], array[pivot - 1]];
large = [array[pivot], array[len - 1]];
console.log(small, large);
} else {
console.log("Not possible");
}
Well I came up with the following solution. Considering that array is always sorted
function GetVals(arr, pivot){
return arr.reduce(function(t,v,i,arr){
if (v < pivot) {
if (typeof t.LMin == "undefined" || t.LMin > v)
t.LMin = v;
if (typeof t.LMax == "undefined" || t.LMax < v)
t.LMax = v;
} else if (v > pivot) {
if (typeof t.RMin == "undefined" || t.RMin > v)
t.RMin = v;
if (typeof t.RMax == "undefined" || t.RMax < v)
t.RMax = v;
}
return t;
}, {});
}
The returned result will have 4 properties, LMin LMax for left min and max and RMin RMax for right min maxes respectively.
if LMin and LMax are equal, this means there is only 1 value from the left (the same rule for the right)
if LMin and LMax are undefined, this means that there are no values from the left less than pivot.
EDIT
Although I realize the question has been answered and another response selected, I at least wanted to update my code to a tested version. FWIW, it works, and does not rely on nor assume a sorted array.
function minmax(valueArray, targetValue) {
var i = 0;
var minBelow;
var maxBelow;
var minAbove;
var maxAbove;
while (i < valueArray.length) {
var currentValue = valueArray[i];
if (currentValue < targetValue) {
if (currentValue < minBelow || !minBelow) {
minBelow = currentValue;
}
if (currentValue > maxBelow || !maxBelow) {
maxBelow = currentValue;
}
}
if (currentValue > targetValue) {
if (currentValue < minAbove || !minAbove) {
minAbove = currentValue;
}
if (currentValue > maxAbove || !maxAbove) {
maxAbove = currentValue;
}
}
i++;
}
return {
minBelow: minBelow,
maxBelow: maxBelow,
minAbove: minAbove,
maxAbove: maxAbove
};
}
function test() {
alert('In test');
var foo = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var bar = minmax(foo, 5);
alert(bar.minBelow + ","+ bar.maxBelow + "," + bar.minAbove + "," + bar.maxAbove);
}

Categories

Resources