I'm trying to solve problem of day 9 of the advent of code in javascript.
I'm using backtracking for getting all possible routes and then calculate the cost of each one.
I'm used to do backtracking in languages like PHP and C++, but never did in JS, so I've found thanks to google that you can't pass a mutable parameter like the & parameters in both PHP and C++.
My intent is to assign the bestRoute variable to the best route because that is the solution of the problem.
But when I do that, using return in some sites, I get an undefined variable error like this:
for (var i = 0 ; i < neighborsArray.length ; i++) {
^
TypeError: Cannot read property 'length' of undefined at getCostInNeighbors (/home/freinn/librosjavascript/advent_of_code/day9.js:117:40)
at calculateCost (/home/freinn/librosjavascript/advent_of_code/day9.js:110:17)
Here is my current code, that does not work and prints bestRoute like the first defined.
"use strict";
function clone(obj) {
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) {
copy[attr] = clone(obj[attr]);
}
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
function generateGraphAsArray(input) {
var lines = input.split("\n");
var getFromTo = /(.*?) to (.*?) = (\d+)/;
var graph = {};
var matches = [];
for (var i = 0 ; i < lines.length ; i++) {
matches = getFromTo.exec(lines[i]);
// console.log(matches[1], matches[2], matches[3]);
var obj = {};
obj['to'] = matches[2];
obj['cost'] = matches[3];
var clonated = clone(obj);
if (!(matches[1] in graph)) {
graph[matches[1]] = [];
}
graph[matches[1]].push(clonated);
if (!(matches[2] in graph)) {
graph[matches[2]] = [];
}
obj['to'] = matches[1];
clonated = clone(obj);
graph[matches[2]].push(clonated);
}
var keys = Object.keys(graph);
var graphArray = [];
var nodeList;
// recuerda: en JS la funcion que no devuelve nada, devuelve algo, undefined
// si no ponemos return en la funcion del map, dara undefined
for (var prop in graph) {
// esto es una clausura sana para poder usar keys
nodeList = graph[prop].map(function(obj) {
return nodeObjectToArray(keys, obj);
});
graphArray.push(nodeList);
}
return graphArray;
}
function nodeObjectToArray(keys, obj) {
var array = new Array(keys.indexOf(obj.to), Number(obj.cost));
return array;
}
function generateAllRoutes(numberOfNodes, graphArray) {
var routes = [];
for (var i = 0 ; i < numberOfNodes; i++) {
var array = [i]
routes.push(array);
}
var bestRoute = generateRangeWithoutUsed([], numberOfNodes);
for (var i = 0 ; i < routes.length; i++) {
bestRoute = generateRoutes(routes[i], numberOfNodes, bestRoute, graphArray);
}
console.log(bestRoute, calculateCost(bestRoute, graphArray));
}
function calculateCost(route, graphArray) {
var limit = graphArray.length - 1;
var cost = 0;
for (var i = 0 ; i < limit ; i++) {
cost += getCostInNeighbors(graphArray[route[i]], route[i+1]);
}
return cost;
}
function getCostInNeighbors(neighborsArray, neighbour) {
for (var i = 0 ; i < neighborsArray.length ; i++) {
if (neighborsArray[i][0] == neighbour) {
return neighborsArray[i][1];
}
}
}
function generateRoutes(currentRoute, numberOfNodes, bestRoute, graphArray) {
if (currentRoute.length == numberOfNodes) {
var currentRouteCost = calculateCost(currentRoute, graphArray);
console.log(currentRouteCost);
if (currentRouteCost < calculateCost(bestRoute, graphArray)) {
return currentRoute;
} else {
return bestRoute;
}
} else {
var possibleNextNodes = generateRangeWithoutUsed(currentRoute, numberOfNodes);
for (var i = 0 ; i < possibleNextNodes.length ; i++) {
currentRoute.push(possibleNextNodes[i]);
generateRoutes(currentRoute, numberOfNodes, bestRoute, graphArray);
currentRoute.splice(-1, 1); // remove the last element
}
}
}
function generateRangeWithoutUsed(used, numberOfNodes) {
var rangeWithoutUsed = [];
for (var i = 0 ; i < numberOfNodes ; i++) {
if (!existInArray(i, used)) {
rangeWithoutUsed.push(i);
}
}
return rangeWithoutUsed;
}
function existInArray(element, array) {
for (var i = 0 ; i < array.length ; i++) {
if (array[i] == element) {
return true;
}
}
return false;
}
var input = "Faerun to Norrath = 129\nFaerun to Tristram = 58\nFaerun to AlphaCentauri = 13\nFaerun to Arbre = 24\nFaerun to Snowdin = 60\nFaerun to Tambi = 71\nFaerun to Straylight = 67\nNorrath to Tristram = 142\nNorrath to AlphaCentauri = 15\nNorrath to Arbre = 135\nNorrath to Snowdin = 75\nNorrath to Tambi = 82\nNorrath to Straylight = 54\nTristram to AlphaCentauri = 118\nTristram to Arbre = 122\nTristram to Snowdin = 103\nTristram to Tambi = 49\nTristram to Straylight = 97\nAlphaCentauri to Arbre = 116\nAlphaCentauri to Snowdin = 12\nAlphaCentauri to Tambi = 18\nAlphaCentauri to Straylight = 91\nArbre to Snowdin = 129\nArbre to Tambi = 53\nArbre to Straylight = 40\nSnowdin to Tambi = 15\nSnowdin to Straylight = 99\nTambi to Straylight = 70";
// var myInput = "a to b = 1\na to c = 2\nb to d = 7\nc to d = 1\nc to e = 3\nd to f = 2\ne to f = 5";
var graphArray = generateGraphAsArray(input);
generateAllRoutes(graphArray.length, graphArray);
In generateRoutes, if your else case happens, the function returns null (because there is no return statement), wicht sets bestRoute to null, wich causes an error the next time calculateCost gets called
Related
I was making a HashTable to have as an example and have it saved for any problem, but I ran into a problem trying to implement a method that returns true or false in case the value belongs to the HashTable, since it is inside a arrays of objects as comment in the code.
I have tried for loops, .map and for of, but it always fails, if someone could help me.
function HashTable () {
this.buckets = [];
this.numbuckets = 35;
}
HashTable.prototype.hash = function (key) {
let suma = 0;
for (let i = 0; i < key.length; i++) {
suma = suma + key.charCodeAt(i);
}
return suma % this.numbuckets;
}
HashTable.prototype.set = function (key, value) {
if (typeof key !== "string") {
throw new TypeError ("Keys must be strings")
} else {
var index = this.hash(key);
if(this.buckets[index] === undefined) {
this.buckets[index] = {};
}
this.buckets[index][key] = value;
}
}
HashTable.prototype.get = function (key) {
var index = this.hash(key);
return this.buckets[index][key];
}
HashTable.prototype.hasKey = function (key) {
var index = this.hash(key);
return this.buckets[index].hasOwnProperty(key)
}
HashTable.prototype.remove = function (key) {
var index = this.hash(key);
if (this.buckets[index].hasOwnProperty(key)) {
delete this.buckets[index]
return true;
}
return false;
}
HashTable.prototype.hasValue = function (value) {
let result = this.buckets;
result = result.flat(Infinity);
return result // [{Name: Toni}, {Mame: Tino}, {Answer: Jhon}]
}
You can use Object.values() to get the values of all the propertyies in the bucket.
function HashTable() {
this.buckets = [];
this.numbuckets = 35;
}
HashTable.prototype.hash = function(key) {
let suma = 0;
for (let i = 0; i < key.length; i++) {
suma = suma + key.charCodeAt(i);
}
return suma % this.numbuckets;
}
HashTable.prototype.set = function(key, value) {
if (typeof key !== "string") {
throw new TypeError("Keys must be strings")
} else {
var index = this.hash(key);
if (this.buckets[index] === undefined) {
this.buckets[index] = {};
}
this.buckets[index][key] = value;
}
}
HashTable.prototype.get = function(key) {
var index = this.hash(key);
return this.buckets[index][key];
}
HashTable.prototype.hasKey = function(key) {
var index = this.hash(key);
return this.buckets[index].hasOwnProperty(key)
}
HashTable.prototype.remove = function(key) {
var index = this.hash(key);
if (this.buckets[index].hasOwnProperty(key)) {
delete this.buckets[index]
return true;
}
return false;
}
HashTable.prototype.hasValue = function(value) {
return this.buckets.some(bucket => Object.values(bucket).includes(value));
}
let h = new HashTable;
h.set("Abc", 1);
h.set("Def", 2);
console.log(h.hasValue(1));
console.log(h.hasValue(3));
function url_info()
{
var url_val=document.getElementsByClassName("spc-tab");
var current_s=0;
for(var i=0;i<url_val.length;i++)
{
var url_class=url_val[i].className.split(" ");
if(url_class[1]!=null)
{
if(url_class[1]=="selected")
{
current_s=i;
break;
}
}
}
var temp_1=url_val[current_s].text; //**Error here**
return(temp_1);
}
In this function url_info i am getting the TypeError But i don't know why?? .... as My var current_s is defined within the scope and integer...
why write this much of code when one line can do:
function url_info() {
var temp_1 = "";
var url_val = document.querySelector(".spc-tab.selected");
temp_1 = url_val[0].text > 0 ? url_val[0].text : temp_1;
return (temp_1);
}
and second, you don't require to convert your class name to string then split. You just can access them through classlist. Then use contains.
function url_info() {
var url_val = document.getElementsByClassName("spc-tab");
var current_s = 0;
for (var i = 0; i < url_val.length; i++) {
var isSelected = url_val[i].classList.contains("selected");
if (isSelected) {
current_s = i;
break;
}
}
var temp_1 = url_val[current_s].text;
return (temp_1);
}
Let's say I've got the following object:
Variables.settings.backend.url = 'http://localhost';
What I would do to test is url is available, is do to the following:
if ('settings' in Variables && 'backend' in Variables.settings && 'url' in Variables.settings.backend) {
// true
}
This is quite cumbersome.
If this was PHP, i could just do
if (!empty($variables['settings']['backend']['url']) {
//true
}
Can this be done any simpler in JS?
I wrote a function to test this :
var isModuleDefined = function(moduleName) {
var d = moduleName.split(".");
var md = d[0];
for (var i = 1, len = d.length; i <= len; i++) {
if (eval("typeof " + md) !== "undefined") {
md = md + "." + d[i];
continue;
}
break;
}
return i === len + 1;
};
isModuleDefined("Variables.settings.backend.url");
but i really don't know about the cost-efficiency of that method, using eval.
Edit (Without eval..):
var isModuleDefined = function(moduleName) {
var d = moduleName.split(".");
var base = window;
for (var i = 0, len = d.length; i < len; i++) {
if (typeof base[d[i]] != "undefined") {
base = base[d[i]];
continue;
}
break;
}
return i === len;
};
Yet another variant
function isFieldExist(expression){
var fields = expression.split('.'),
cur = this;
for(var i=0; i<fields.length; i++){
if(typeof cur[fields[i]] === "undefined") return false;
cur = cur[fields[i]];
}
return true;
}
for using
isFieldExist("Variables.settings.backend.url"); // find in global scope
isFieldExist.call(Variables, "settings.backend.url"); // find in Variables
This code is designed to identify an array of anagrams for a string given an array of possible anagrams.
var anagram = function(input) {
return input.toLowerCase();
}
I'm adding the matcher function here to the String prototype.
String.prototype.matcher = function(remainingLetters) {
var clone = this.split("");
for (var i = 0; i < clone.length; i++) {
if (clone[i].indexOf(remainingLetters) > -1) {
remainingLetters.splice(clone[i].indexOf(remainingLetters, 1));
clone.splice(i, 1);
}
}
if (remainingLetters.length == 0 && clone.length == 0) {
return true;
}
else {
return false;
}
}
a
String.prototype.matches = function(matchWordArray) {
var result = [];
for (var i = 0; matchWordArray.length; i++) {
var remainingLetters = this.split("");
if (matchWordArray[i].matcher(remainingLetters)) {
result.push(arrayToMatch[i]);
}
}
return result;
}
var a = anagram("test");
a.matches(["stet", "blah", "1"]);
module.exports = anagram;
Should probably be:
for (var i = 0; i < matchWordArray.length; i++) {
The original statement:
for (var i = 0; matchWordArray.length; i++) {
...would result in an infinite loop because matchWordArray.length is always truthy (3) in your test.
var place = []
place[0] = "Sant Marti de Canals";
place[1] = "Catalonia";
place[3] = "";
place[4] = "Spain";
placeTitle = place.join(",");
current output is "Sant Marti de Canals,Catalonia,,,Spain"
how can it be "Sant Marti de Canals,Catalonia,Spain"
You can also define a filter function, which is useful in many other situations.
Array.prototype.filter = function(p) {
var a = [];
p = p || function (x) {return x;}
for (var i = 0; i<this.length; ++i) {
if (p(this[i])) {
a.push(this[i]);
}
}
return a;
}
...
placeTitle = place.filter().join(', ');
Writing your own code for this can help
var place = []
place[0] = "Sant Marti de Canals"; place[1] = "Catalonia"; place[3] = ""; place[4] = "Spain";
var placeTitle ='';
for(var test in place) {
placeTitle += place[test]!=''?place[test]+',':'';
}
placeTitle = placeTitle.substr(0,placeTitle.length-1)
placeCopy=[];
for(var i=0;i<place.length;i++){
if(typeof place[i] != 'undefined' && place[i].length > 0){
placeCopy.push(place[i]);
}
}
placeTitle = placeCopy.join(",");