Porting breadth-first search to Javascript - javascript

I created AI for snake in Python. The game works nicely. I started learning Javascript recently and I tried writting the Javascript equivalent of the game.
The game is played on x*y grid (eg. 30*20). In Python I used (x, y) tuples for game position. In JS I use integers which I map ussing:
function map(x, y) {
return x + y * size.width;
}
function unmap(pos) {
return {x: pos % size.width, y: Math.floor(pos/size.width)};
}
My problem is that the search doesn't work. When I try to create path from it enters infinite loop. The search function is:
function search(start, goal) {
var frontier = new PriorityQueue({
comparator: function(a, b) {
return a.score - b.score;
}
});
frontier.queue({value: start, score: 0});
var cameFrom = {};
cameFrom[start] = null;
while (frontier.length !== 0) {
var current = frontier.dequeue().value;
if (current === goal) {
break;
}
var nbs = neighbors(current);
for(var i = 0; i < nbs.length; i++) {
var next = nbs[i];
if (Object.keys(cameFrom).indexOf(next) === -1) {
var priority = heuristic(goal, next);
frontier.queue({value: next, score: priority});
cameFrom[next] = current;
}
}
}
return cameFrom;
}
I use this priority queue.
The search in Python is more OOP but I don't want to include more code - the question is already long. But I'll include the search:
def search(self, grid, start, goal):
frontier = PriorityQueue()
frontier.put(start, 0)
came_from = {}
came_from[start] = None
while not frontier.empty():
current = frontier.get()
if current == goal:
break
for next in grid.neighbors(current):
if next not in came_from:
priority = self.heuristic(goal, next)
frontier.put(next, priority)
came_from[next] = current
return came_from
If anything more is needed please ask. I'm bad at JS.

Problem was with this line:
if (Object.keys(cameFrom).indexOf(next) === -1) {
I was searching for integer but keys are always strings. You can clearly the behavior in this example:
var foo = {0: "fooBar", 1: "bar"};
console.log("keys", Object.keys(foo));
console.log("int", Object.keys(foo).indexOf(0)); // -1
console.log("string", Object.keys(foo).indexOf("0")); // 0
This is more readable, shorter and works:
if (cameFrom[next] === undefined) {
Thanks to IVlad who pointed out the bad line.

Related

How do I optimize this synchronous "fuzzy search" function and turn it into an async function?

Problem
I'm trying to implement some sort of "fuzzy search" in my Node.js based project.
Fuzzy search is a search that returns results even if the string didn't match exactly.
I found this code in another stackoverflow thread. Code is below.
It's quite good, but the problem is - it's synchronous It slows down the whole program when it searches through a large array.
Question
ES6 methods are welcomed. Needs to work only in the latest Chrome, so any JS methods will work.
Are there any new JS methods that would optimize this function?
Am I doing something wrong there that makes it even slower?
Can I turn this function into an async function that returns a promise? Will it stop freezing the app during the search then?
Are there any better "fuzzy search" implementations you know of? (I found a module called fuzzysort, can't say if it's that much better though, it won't return "folder test" if you type "test folder" (wrong order) so it's not that good)
Code
Calling search function
searchArray is an array of paths it searches through, e.g.: ["C:\\test", "C:\\file.txt"...] (0.5 - 5 million paths)
searchQuery is a string without spaces, e.g.: filetxt
search () {
const fuzzySearch = this.fuzzySearch(this.searchQuery.toLowerCase(), this.searchArray)
let result = fuzzySearch.filter(element => element.relevance >= 0.3)
// sort by relevance
var sortedResults = result.sort((a, b) => parseFloat(b.relevance) - parseFloat(a.relevance)).map(item => item.name);
this.searchResults = sortedResults
},
The fuzzy search function
fuzzySearch (searchQuery, searchArray) {
const get_bigrams = function(string) {
const s = string.toLowerCase();
const v = new Array(s.length - 1);
for (let i = 0, end = v.length; i <= end; i++) {
v[i] = s.slice(i, i + 2);
}
return v;
};
const string_similarity = function(str1, str2) {
if ((str1.length > 0) && (str2.length > 0)) {
const pairs1 = get_bigrams(str1);
const pairs2 = get_bigrams(str2);
const union = pairs1.length + pairs2.length;
let hit_count = 0;
for (let x of Array.from(pairs1)) {
for (let y of Array.from(pairs2)) {
if (x === y) {
hit_count++;
}
}
}
if (hit_count > 0) {
return ((2.0 * hit_count) / union);
}
}
return 0.0;
};
let results = [];
for (let name of searchArray) {
// I added .match to use only the base filename (name+ext) not the whole path, and removed all characters
let filteredPath = name.match(/[^\\\/]+$/)[0].replace(/[^A-Za-z0-9.]+/g, '')
const relevance = string_similarity(searchQuery, filteredPath);
const obj = {name, relevance};
results.push(obj);
}
return results
},

Issues with a recursive function when trying to create a BST using Pseudoclassical Inheritance

So I'm trying to create a Binary Search Tree using Pseudoclassical inheritance. It accepts an array, sorts it, uses the middle value as the starting point, and inserts the remaining values from the array into the BST. I guess I'm trying my best to utilize functional programming (correct me if I'm wrong please) by using reuseable methods and also because a BST insert method needs to be recursive.
I've pointed out where the code errors out. I believe it takes 3 as the initial value, I also believe 1 (the next value in the array) successfully gets inserted, but I believe the number 2 is where the error occurs when it says that "TypeError: this.left.insert is not a function". Can anyone point out what I'm doing wrong? Why won't the insert method call itself for this.left?
var NoDuplicatesBST = function(array) {
var tempArr = arguments[0].sort(function(a, b) {
return a-b;
});
var middle = Math.floor(((tempArr.length - 1) / 2));
var sliced = tempArr.splice(middle, 1);
this.createBST(sliced[0]);
// now insert the rest of tempArr into the BST
for (var i = 0; i < tempArr.length; i++) {
this.insert(tempArr[i]);
}
};
NoDuplicatesBST.prototype.createBST = function(number) {
this.value = number;
this.left = null;
this.right = null;
};
NoDuplicatesBST.prototype.insert = function(number) {
if (number < this.value) {
if (this.left === null) {
this.left = new this.createBST(number);
} else {
// ------------CODE BELOW DOES NOT WORK!, LINED 77 ALSO PROBABLY. TypeError: this.left.insert is not a function----------------------
this.left.insert(number);
}
} else if (number > this.value) {
if (this.right === null) {
this.right = new this.createBST(number);
} else {
this.right.insert(number);
}
} else {
// Do nothing
}
};
var testBST = new NoDuplicatesBST([2,3,4,5,1]);
console.log("The testBST:", testBST);
That's not written in functional way, take a look and try to go thru this tutorial to learn more about functional programming in JS: http://reactivex.io/learnrx/
And to the original question why you see the "TypeError: this.left.insert is not a function". Check my comments in your code:
var NoDuplicatesBST = function(arr) {
var middle, left = [], center, right = [];
if (!Array.isArray(arr) || arr.length == 0) {
return this;
}
if (arr.length == 1) {
center = arr[0];
} else {
middle = Math.floor((arr.length / 2));
center = arr[middle];
left = arr.slice(0, middle);
right = arr.slice(middle + 1, arr.length);
console.log('left:', left);
console.log('middle:', center);
console.log('right:', right);
}
this.createBST(center);
// now insert left and right parts to BST
if (left.length > 0) {
this.insert(left);
}
if (right.length > 0) {
this.insert(right);
}
};
NoDuplicatesBST.prototype.createBST = function(number) {
this.value = number;
this.left = null;
this.right = null;
};
NoDuplicatesBST.prototype.insert = function(arr) {
if (arr.length > 0) {
//array is sorted and we took the middle element, so we can compare just the first element
if (arr[0] < this.value) {
/** Here you use createBST as a constructor, it creates a new element,
with left and right values, but you break the prototypal inheritance chain,
that's why you don't have access to the insert function */
// this.left = new this.createBST(number);
// it's better to pass the part of the array to build the tree further
this.left = new NoDuplicatesBST(arr);
} else {
this.right = new NoDuplicatesBST(arr); //the same as above
}
}
};
var arr = [2, 3, 4, 5, 1];
var tempArr = arr.reduce(function (noDuplicatesArr, current) { //remove duplicates
if (noDuplicatesArr.indexOf(current) === -1) {
noDuplicatesArr.push(current);
}
return noDuplicatesArr;
}, []).sort(function(a, b) {
return a - b;
});
var testBST = new NoDuplicatesBST(tempArr);
console.log("The testBST:", testBST);
For prototypal chain inheritance check: https://developer.mozilla.org/en/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
BTW. I changed your code to accept arrays instead of numbers, now that will build a BST

NodeJS require with asynch functions when synch is wanted

I have the following code
var utils = require(`${__dirname}/../../utils/utils.js`);
...
let object = utils.parse(input);
if (object === undefined){
let helper = utils.recognize(input);
msg.channel.sendMessage("\"" + input + "\" not recognized. Did you mean \"" + helper[0] + "\"?");
object = utils.parse(helper[0]);
}
//code related to object
console.log(object.strLength);
where "parse" tries to match the input to an object in a database, and "recognize" tries to find the best match if the input is spelled incorrectly (Levenshtein) (along with additional info such as how close the match was).
Currently the issue is that the code is ran asynchronously; "object.strLength" returns an undefined before utils.recognize() returns a value. If I copy/paste the recognize() and parse() functions into the file, then the code is run synchronously and I do not run into any issues. However I would rather keep those functions in a separate file as I reuse them in other files.
Is there a way to specify that the functions in utils must be synch? I know that there are libraries that convert asynch into synch but I prefer to use as few libraries as I can help it. I tried to have the recognize functions return a Promise but it ended up as a jumbled mess
edit: here's parse. I did not think it was necessary to answer this question so I did not include it initially:
var db = require(`${__dirname}/../data/database.js`);
...
var parse = (input) => {
let output = db[output];
if (output === null) {
Object.keys(db).forEach((item) => {
if (db[item].num === parseInt(input) || (db[item].color + db[item].type === input)){
output = db[item];
return false;
}
});
}
return output;
}
I solved the issue, thanks everyone. Here's what was wrong, it was with recognize(). It was my mistake to not show the code for it initially.
Original recognize:
var recognize = (item) => {
//iterate through our databases and get a best fit
let bestItem = null;
let bestScore = 99999; //arbitrary large number
//let bestType = null;
//found algorithm online by milot-mirdita
var levenshtein = function(a, b) {
if (a.length == 0) { return b.length; }
if (b.length == 0) { return a.length; }
// swap to save some memory O(min(a,b)) instead of O(a)
if(a.length > b.length) {
let tmp = a;
a = b;
b = tmp;
}
let row = [];
for(let i = 0; i <= a.length; i++) {
row[i] = i;
}
for (let i = 1; i <= b.length; i++) {
let prev = i;
for (let j = 1; j <= a.length; j++) {
let val;
if (b.charAt(i-1) == a.charAt(j-1)) {
val = row[j-1]; // match
} else {
val = Math.min(row[j-1] + 1, // substitution
prev + 1, // insertion
row[j] + 1); // deletion
}
row[j - 1] = prev;
prev = val;
}
row[a.length] = prev;
}
return row[a.length];
}
//putting this here would make the code work
//console.log("hi");
Object.keys(db).forEach((key) => {
if (levenshtein(item, key) < bestScore) {
bestItem = key;
bestScore = levenshtein(item, key);
}
});
return [bestItem, bestScore];
}
My solution was to move the levenshtein function outside of the recognize function, so if I wanted to I can call levenshtein from another function
#user949300 and #Robert Moskal, I changed the forEach loop into a let...in loop. There is no functional difference (as far as I can tell) but the code does look cleaner.
#Thomas, I fixed the let output = db[output]; issue, oops.
Again, thanks for all of your help, I appreciate it. And happy New Year too

How to sort and slice an array of objects

I have an array of shots. I have been able to take that array and loop through it to get all shots that occurred on hole #1 and then rearrange them in order based on "shot_number". I now need to do this for every hole and to create an array for each hole (ex: holeArray1, holeArray2). I have attempted a number of solutions to increment x but if I do I end up missing some shots that occurred on certain holes.
How can I refactor this function to create this array for every hole without just copying and pasting the code and changing the variable x myself? Thank you for your help. I know I should be able to figure this one out but am struggling.
$scope.createHoleShotsArrays = function () {
var i = 0;
var x = 1;
var holeArray = [];
var len = $scope.shots.length;
for (; i < len; i++) {
if ($scope.shots[i].attributes.hole == x) {
holeArray.push($scope.shots[i]);
holeArray.sort(function (a, b) {
if (a.attributes.shot_number > b.attributes.shot_number) {
return 1;
}
if (a.attributes.shot_number < b.attributes.shot_number) {
return -1;
}
// a must be equal to b
return 0;
});
}
}
console.log(holeArray);
};
Push the items you want into arrays, and sort them once. I don't have cases to test the code. You may modified it a little if something goes wrong.
$scope.createHoleShotsArrays = function() {
var holeArrays = [];
$scope.shots.forEach(function(shot) {
if (holeArrays.length < shot.attributes.hole) {
holeArrays[shot.attributes.hole - 1] = [];
}
holeArrays[shot.attributes.hole - 1].push(shot);
});
holeArrays.forEach(function(arr) {
arr.sort(function(a, b) {
return a.attributes.shot_number - b.attributes.shot_number;
});
});
console.log(holeArrays);
};

What is the fastest way to check whether a specific UserID exists in the array using Jquery or Javascript

I have an array of objects gAllMedicalFilesClaimantsArray with 2 properties (UserID & UserInfo)
For example:
gAllMedicalFilesClaimantsArray[0].UserID = "111";
gAllMedicalFilesClaimantsArray[0].UserInfo = "AAA-111";
gAllMedicalFilesClaimantsArray[1].UserID = "222";
gAllMedicalFilesClaimantsArray[1].UserInfo = "BDD-478333";
What is the fastest way to check whether a specific UserID exists in the array using Jquery or Javascript because gAllMedicalFilesClaimantsArray has got 8000 records?
Thanks
var match = '222';
var matches = $.grep(myArray, function(el, index) {
return (el.UserID === match);
});
You can fasten the search process by using Binary Search algorithm if the array is sorted (e.g with respect to UserId).
function binarySearch(array, userid) {
var low = 0, high = array.length - 1,
i, comparison;
while (low <= high) {
i = parseInt((low + high) / 2, 10);
if (array[i].UserId < userid) { low = i + 1; continue; };
if (array[i].UserId > userid) { high = i - 1; continue; };
return array[i];
}
return null;
};
You can find the user of which ID is 12 by using the function:
var result = binarySearch(gAllMedicalFilesClaimantsArray, 12);
Something like this, I believe:
function exists(uid) {
var k = gAllMedicalFilesClaimantsArray.length;
uid = uid.toString(); // ensure the arg is a str (this can be omitted)
while (k--) {
if (gAllMedicalFilesClaimantsArray[k].UserID === uid) {
return true;
}
}
return false;
}
Is the array sorted by the UserID? If so, it can be improved either further by using a binary search; that would change this from O(n) to O(log n). Your example suggests it is. I found a good implementation of a binary search in JavaScript on the web, here. Here is the code if the site ever dies:
function binarySearch(items, value){
var startIndex = 0,
stopIndex = items.length - 1,
middle = Math.floor((stopIndex + startIndex)/2);
while(items[middle] != value && startIndex < stopIndex){
//adjust search area
if (value < items[middle]){
stopIndex = middle - 1;
} else if (value > items[middle]){
startIndex = middle + 1;
}
//recalculate middle
middle = Math.floor((stopIndex + startIndex)/2);
}
//make sure it's the right value
return (items[middle] != value) ? -1 : middle;
}
ExistsInArray(value, array){
for(var item in array){
if(item.UserId == value){
return true;
}
}
return false;
}
You can either prototype Array object, like this:
Array.prototype.exists = function(value, prop){
var i = null;
for (i in this)
if (this[i][prop] && this[i][prop] == value)
return true;
return false;
}
gAllMedicalFilesClaimantsArray.exists('222', 'UserID');

Categories