Javascript Array Sorting and splicing - javascript

I am making a game and I need sprites to find the closest "food" and head towards them. I am having trouble finding the right code to make this happen. They seem to get the first food just fine but when there are multiple food items the sprite is indecisive between each one and doesn't go far enough to eat any of them. Still don't have much deep experience with arrays to understand.
I'm using cocos2d framework.
update:function (dt) {
for (var i = 0; i < this._gubz.length; i++)
{
var gub = this._gubz[i];
if (this._food.length>0)
{
var distances = new Array();
for (var w = 0; w < this._food.length; w++)
{
var food = this._food[w];
var location = food.getPosition();
var distance = cc.pDistance(location,gub.getPosition());
}
var smallestDistance = Math.min.apply(Math,distances);
var indexOfNumber = distances.indexOf(smallestDistance);
var closestFood = this._food[indexOfNumber];
gub.currentTarget = indexOfNumber;
}
}
for (var i = 0; i < this.foodToRemove.length; i++)
{
this.removeChild(this.foodToRemove[i].GetUserData());
this._food.splice(this._food.indexOf(this.foodToRemove[i].GetUserData()),1)
this.world.DestroyBody(this.foodToRemove[i]);
}
}

If you want to apply sorting to a javascript array use the sort variable.
this._food = this._food.sort(function(a, b) {
return a.smallestDistance > b.smallestDistance ? -1 : a.smallestDistance < b.smallesDistance ? 1 : 0;
});
to remove items use the filter item:
this._food = this._food.filter(function(element) {
return elementDoesNotExist(element);
});
Should get you started.
HTH

Related

Javascript file isn't appending to a filtered list

As the title reads, my javascript file won't append to a filtered list via a random generator function, for context it's being built in replit so perhaps its just a buggy bit of software on replits end, though I'm certain I've done something wrong and can't for the life of me figure out what. The main idea for this project is to have it randomly select an item from a list, append it to an empty list, then pass that string to an HTML textarea tag to be displayed as text.
Code in question:
var LowercaseList = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"];
var NumList = ["1","2","3","4","5","6","7","8","9","0"];
var SpecialCharList = ["`","~","!","#","$","%","^","&","*","_","-","+","=","<",">","?","/"];
// Letter list ID's since I couldn't figure out how to run this using a database
var LetterIDList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26];
// Number list ID's since I couldn't figure out how to run this using a database
var NumIDList = [1,2,3,4,5,6,7,8,9,10];
// Special character list ID's since I couldn't figure out how to run this using a database
var SpecialID = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
// Filtered Lists
var UpperFiltered = [];
var LowerFiltered = [];
var NumFiltered = [];
var SpecialFiltered = [];
// Global declarations of random number variables to grab values from intial lists
var UpperGenVariable;
var LowerGenVariable;
var NumGenVariable;
var SpecialGenVariable;
// Creates a basic password with 2 of each kind of character
function Basic () {
for (var i = 1; i < 2; i++) {
UppercaseGenerate();
}
for (var i = 1; i < 2; i++) {
LowercaseGenerate();
}
for (var i = 1; i < 2; i++) {
NumGenerate();
}
for (var i = 1; i < 2; i++) {
SpecialGenerate();
}
};
let passwordGen = document.getElementById("PasswordDisplay")
function UppercaseGenerate () {
var UpperFiltered = [];
UpperGenVariable = Math.random(0, LetterIDList.length);
for (var i = 0; i < LetterIDList.length - 1; i++) {
if (LetterIDList[i] == UpperGenVariable) {
appendItem(UpperFiltered, UppercaseList[i]);
}
};
console.log(UpperFiltered);
passwordGen.value = UpperFiltered
};
HTML textarea code:
<label style = "color:white;" for="PasswordDisplay">Your generated password ----></label>
<textarea readonly id="PasswordDisplay" name="PasswordDisplay" rows="10" cols="50">
Please select a password complexity setting
</textarea>
I changed how you generate the random number.
Also added the UppercaseList, added i <= 2 rather then i < 2 in the cycles so the yould really run 2 times, added += to passwordGen.value += UpperFiltered so the value would add up.
I guess that is it.
var UppercaseList = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
var LowercaseList = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"];
var NumList = ["1","2","3","4","5","6","7","8","9","0"];
var SpecialCharList = ["`","~","!","#","$","%","^","&","*","_","-","+","=","<",">","?","/"];
// Letter list ID's since I couldn't figure out how to run this using a database
var LetterIDList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26];
// Number list ID's since I couldn't figure out how to run this using a database
var NumIDList = [1,2,3,4,5,6,7,8,9,10];
// Special character list ID's since I couldn't figure out how to run this using a database
var SpecialID = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
// Filtered Lists
var UpperFiltered = [];
var LowerFiltered = [];
var NumFiltered = [];
var SpecialFiltered = [];
// Global declarations of random number variables to grab values from intial lists
var UpperGenVariable;
var LowerGenVariable;
var NumGenVariable;
var SpecialGenVariable;
// Creates a basic password with 2 of each kind of character
function Basic () {
for (var i = 1; i <= 2; i++) {
UppercaseGenerate();
}
for (var i = 1; i <= 2; i++) {
LowercaseGenerate();
}
for (var i = 1; i <= 2; i++) {
NumGenerate();
}
for (var i = 1; i <= 2; i++) {
SpecialGenerate();
}
};
let passwordGen = document.getElementById("PasswordDisplay")
function UppercaseGenerate() {
var UpperFiltered = [];
UpperGenVariable = Math.floor(Math.random() * LetterIDList.length);
for (var i = 0; i < LetterIDList.length; i++) {
if (LetterIDList[i] == UpperGenVariable) {
UpperFiltered.push(UppercaseList[i]);
}
};
passwordGen.value += UpperFiltered
};

Manipulate more javascript array based on another array

I've a strange thing to do but I don't know how to start
I start with this vars
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
So to start all the 3 array have the same length and the very first operation is to see if there is a duplicate value in sky array, in this case the 0 is duplicated and only in this case is at the end, but all of time the sky array is sorted. So I've to remove all the duplicate (in this case 0) from sky and remove the corresponding items from base and sum the corresponding items on ite. So if there's duplicate on position 4,5 I've to manipulate this conditions. But let see the new 3 array:
var new_base = [1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var new_sky = [0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var new_ite = [139,38,13,15,6,4,6,3,2,1,2,1,1,1];
If you see the new_ite have 139 instead the 64,52,23, that is the sum of 64+52+23, because the first 3 items on sky are the same (0) so I remove two corresponding value from base and sky too and I sum the corresponding value into the new_ite array.
There's a fast way to do that? I thought a for loops but I stuck at the very first for (i = 0; i < sky.length; i++) lol, cuz I've no idea on how to manipulate those 3 array in that way
J
When removing elements from an array during a loop, the trick is to start at the end and move to the front. It makes many things easier.
for( var i = sky.length-1; i>=0; i--) {
if (sky[i] == prev) {
// Remove previous index from base, sky
// See http://stackoverflow.com/questions/5767325/how-to-remove-a-particular-element-from-an-array-in-javascript
base.splice(i+1, 1);
sky.splice(i+1, 1);
// Do sum, then remove
ite[i] += ite[i+1];
ite.splice(i+1, 1);
}
prev = sky[i];
}
I won't speak to whether this is the "fastest", but it does work, and it's "fast" in terms of requiring little programmer time to write and understand. (Which is often the most important kind of fast.)
I would suggest this solution where j is used as index for the new arrays, and i for the original arrays:
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
var new_base = [], new_sky = [], new_ite = [];
var j = -1;
sky.forEach(function (sk, i) {
if (!i || sk !== sky[i-1]) {
new_ite[++j] = 0;
new_base[j] = base[i];
new_sky[j] = sk;
}
new_ite[j] += ite[i];
});
console.log('new_base = ' + new_base);
console.log('new_sky = ' + new_sky);
console.log('new_ite = ' + new_ite);
You can use Array#reduce to create new arrays from the originals according to the rules:
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
var result = sky.reduce(function(r, n, i) {
var last = r.sky.length - 1;
if(n === r.sky[last]) {
r.ite[last] += ite[i];
} else {
r.base.push(base[i]);
r.sky.push(n);
r.ite.push(ite[i]);
}
return r;
}, { base: [], sky: [], ite: [] });
console.log('new base:', result.base.join(','));
console.log('new sky:', result.sky.join(','));
console.log('new ite:', result.ite.join(','));
atltag's answer is fastest. Please see:
https://repl.it/FBpo/5
Just with a single .reduce() in O(n) time you can do as follows; (I have used array destructuring at the assignment part. One might choose to use three .push()s though)
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330],
sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17],
ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1],
results = sky.reduce((r,c,i) => c === r[1][r[1].length-1] ? (r[2][r[2].length-1] += ite[i],r)
: ([r[0][r[0].length],r[1][r[1].length],r[2][r[2].length]] = [base[i],c,ite[i]],r),[[],[],[]]);
console.log(JSON.stringify(results));

Simulation of mouses moving, don't work

I'm trying to create a simulation of 150 mouses moving inside a 20x20 grid in p5.js (A processing like libary). First I'm spawning 150 mouses random places and everything goes fine. But after I have spawned the mouses I am trying to make them move to one of their neighbors. Instead of moving to one of the neighbors and make the current square empty it stays on the one that it already was one + it moves to the next one so instead of having 150 mouses i suddenly have 300... I have tried changing the code for hours but I can't find the proplem... Here is my code:
var w = 40;
var grid = [];
var mouses = 10;
var mouseAmount = [];
var Mouse;
var current;
function setup() {
createCanvas(800, 800);
cols = floor(width/w)
rows = floor(height/w)
// frameRate(60);
for (var j = 0; j < rows; j++) {
for ( var i = 0; i < cols; i++) {
var cell = new Cells(i,j);
grid.push(cell);
}
}
amount = new Amount;
}
function draw() {
background(51);
for ( var i = 0; i < grid.length; i++) {
grid[i].show();
}
amount.run();
}
function index(i, j) {
if (i < 0 || j < 0 || i > cols-1 || j > rows-1 ) {
return -1;
}
return i + j * cols;
}
function Cells(i, j) {
this.i = i;
this.j = j;
this.active = false;
this.moveCell = function() {
var neighbors = [];
var top = grid[index(i, j -1)];
var right = grid[index(i+1, j)];
var bottom = grid[index(i, j+1)];
var left = grid[index(i-1, j)];
if (top) {
neighbors.push(top)
}
if (right) {
neighbors.push(right)
}
if (bottom) {
neighbors.push(bottom)
}
if (left) {
neighbors.push(left)
}
if(neighbors.length > 0) {
var r = floor(random(0, neighbors.length));
return neighbors[r];
} else {
return undefined;
}
}
this.show = function() {
var x = this.i*w;
var y = this.j*w;
stroke(255);
noFill();
rect(x,y,w,w);
if(this.active == true) {
fill(155, 0, 255, 100)
rect(x, y, w, w)
}
}
}
function Amount() {
this.run = function() {
var r = floor(random(grid.length))
for (var i = 0; i < mouses; i++) {
var mouse = grid[r];
mouseAmount.push(mouse)
}
if (mouseAmount.length < 1499) {
for (var i = 0; i < mouseAmount.length; i++) {
mouseAmount[i].active = true;
}
}
if (mouseAmount.length > 1499) {
Next();
}
}
}
function Next(i,j) {
for (var i = 0; i < mouseAmount.length; i++) {
current = mouseAmount[i];
var nextCell = current.moveCell();
if (nextCell) {
nextCell.active = true;
current.active = false;
current = nextCell;
}
}
}
Thank you in advance :)
I don't really understand exactly what your code is supposed to do, but a few things stand out to me about your code:
Problem One: I don't understand how you're iterating through your grid array. You seem to be iterating over mouseAmount, which seems to hold random cells from the grid for some reason? That doesn't make a lot of sense to me. Why don't you just iterate over the grid array directly?
Problem Two: You then move the cells randomly to a neighbor, but you don't take into account whether the neighbor is already active or not. I'm not sure what you want to happen, but this seems a bit strange.
Problem Three: Usually with simulations like this, you have to copy the next generation into a new data structure instead of modifying the data structure as you step through it.
The biggest problem is that you haven't really explained what you want your code to do, or what this code does instead, or how those two things are different. But if I were you, I'd make the following changes:
Step One: Iterate over your grid array in a more reasonable way. Just iterate over every index and take the appropriate action for every cell. If I were you I would just use a 2D array and use a nested for loop to iterate over it.
Step Two: Make sure your logic for moving to a neighbor is correct. Do you want cells to move to already active cells?
Step Three: Make a copy of the grid before you modify it. Think about it this way: as you iterate over the grid, let's say you move a cell down one row. Then you continue iterating, you'll reach the newly active cell again. In other words, you'll touch the same active cell twice in one generation, which is definitely going to mess you up.
A word of advice: get this working for a single active cell first. It's really hard to tell what's going on since you have so many things going on at one time. Take a step back and make sure it works for one active cell before moving up to having a whole grid.

Quickly and efficiently switch array elements

I am trying to swap the data within these arrays.
My data will look something like this. In production this array can and will be several times bigger.
var data = [
[13.418946862220764, 52.50055852688439],
[13.419011235237122, 52.50113000479732],
[13.419756889343262, 52.50171780290061],
[13.419885635375975, 52.50237416816131],
[13.420631289482117, 52.50294888790448]
]
Currently my switching code looks like the below.
var temp;
for(var i = 0;i < data.length;i++) {
temp = array[i][0];
array[i][0] = array[i][1];
array[i][1] = temp;
}
What I am trying to figure out is if this the most efficient way to do this and/or if any improvements are possible.
Please understand that even the slightest improvement will matter.
I would use a more functional approach:
var switched = data.map(function (arr) {
return [arr[1], arr[0]];
});
If you use ES2015, you can even do that in one line:
const switched = data.map((arr) => [arr[1], arr[0]]);
If you want to stick with a loop:
for(var i = 0; i < data.length; i++) {
data[i] = [data[i][1], data[i][0]];
}
You code looks perfectly fine, and you don't need any further "optimization".
As always, a benchmark is always the good way to find out who is faster:
var arr = (function() {
var res = [];
for(var i = 0; i < 100000; ++i) {
res[i] = [Math.random(), Math.random()];
}
return res;
}());
var swap_in_place = function() {
for(var i = 0; i < arr.length; ++i) {
var tmp = arr[i][0];
arr[i][0] = arr[i][1];
arr[i][1] = tmp;
}
};
var swap_map = function() {
arr = arr.map(function(elem) {return [elem[1], elem[0]]; });
};
var runBench = function(name, f) {
var start = new Date().getTime();
for(var i = 0; i < 50; ++i) {
f();
}
var stop = new Date().getTime();
console.log(name + " took: " + (stop - start));
};
runBench("in_place", swap_in_place);
runBench("map", swap_map);
in my firefox latest (windows 10 x64), I get (quite consistently) 16 for in place, vs 350 for map version, meaning you get a 20x speed down by using map instead of your own version.
You might think this is due to the fact that this snippet is embedded in a iframe and so on, so I ran it in node (4.5.0), which is built on top of V8, and I get the same results:
I think that the Jitter can't be smart enough to properly inline the function in the map version, or deduce it operates on the same memory without side effect. Thefore, the Jitter has to allocate a full new array to store the intermediate results, then loop over it with a function call (meaning register save/restore stall at each iteration), and then either:
copy back the entire data to arr
move the reference (probably what happens), but the garbage collector has to collect the entire temporary array.
The map function might also trigger reallocation of the temporary, which is extremely expensive.

New to JavaScript and think I need a loop for this.....?

I've got a 180 character string of numbers which needed to be broken down into 6 groups, separated into 2 character figures and then sorted into ascending order.
I've done this, but it looks dirty and I'm pretty sure with my gradual improvement of understanding of JavaScript, that a neat little loop would save me a huge amount of repetition.
<script type="text/javascript">
var ticketString = "011722475204365360702637497481233455758302154058881928446789061241507324334876840738576186051132437816395663800818206590104559628214294664710935667287132130687703253151692742547985".match(/.{1,2}/g);
var groupa = ticketString.slice (0,15);
groupa.sort();
var groupb = ticketString.slice (15,30);
groupb.sort();
var groupc = ticketString.slice (30,45);
groupc.sort();
var groupd = ticketString.slice(45,60);
groupd.sort();
var groupe = ticketString.slice(60,75);
groupe.sort();
var groupf = ticketString.slice(75,90);
groupf.sort();
function displayArray () {
document.getElementById('ticketOne').innerHTML = groupa;
document.getElementById('ticketTwo').innerHTML = groupb;
document.getElementById('ticketThree').innerHTML = groupc;
document.getElementById('ticketFour').innerHTML = groupd;
document.getElementById('ticketFive').innerHTML = groupe;
document.getElementById('ticketSix').innerHTML = groupf;
}
The outputs are placed into paragraphs for now, but I know this could be done easier than the way I have it. The function displayArray loads off the body tag.
If you could rename your elements from ticketOne, ticketTwo etc to ticket1, ticket2 etc:
<script type="text/javascript">
var ticketString = "011722475204365360702637497481233455758302154058881928446789061241507324334876840738576186051132437816395663800818206590104559628214294664710935667287132130687703253151692742547985".match(/.{1,2}/g);
for (var i=0; i<ticketString.length/15; i++) {
var group = ticketString.slice(i*15, i*15+15);
group.sort();
document.getElementById('ticket'+(i+1)).innerHTML = group;
}
</script>
So here is what I think is the best solution:
var ticketString = "011722475204365360702637497481233455758302154058881928446789061241507324334876840738576186051132437816395663800818206590104559628214294664710935667287132130687703253151692742547985".match(/.{1,2}/g);
var group = [];
for (var i = 0; i < 180 / 15 / 2; i++) {
group[i] = ticketString.slice(i * 15, (i + 1) * 15);
group[i].sort();
document.getElementById('ticket[' + i + ']').innerHTML = group[i];
}
What I'm doing here is that I created a local array called group, which is in turn easier to handle within a loop than using a name like "group1" or "ticketFirst".
After that I practically did the same you did, but since I used the for loop I was able to short it down significantly.
The reason why I used i < 180 / 15 / 2 is because you're pairing them in character packs of 2. for (var i = 0; i < 180 / 15 / 2; i++) You ofcourse have to have an HTML like this:
<body>
<p id="ticket[0]"></p>
<p id="ticket[1]"></p>
<p id="ticket[2]"></p>
<p id="ticket[3]"></p>
<p id="ticket[4]"></p>
<p id="ticket[5]"></p>
</body>
You can reduce it to:
var groupArray = [];
for(var i=0;i<100;i+=15){
groupArray.push(ticketString.slice (i,15+i));
groupArray[groupArray.length-1].sort();
}
You could do something like this, if you changed your 'ticket' id's to a generic 'ticket' class;
var ticketString = "011722475204365360702637497481233455758302154058881928446789061241507324334876840738576186051132437816395663800818206590104559628214294664710935667287132130687703253151692742547985".match(/.{1,2}/g);
var tickElem = document.querySelectorAll('.ticket');
var strPoint = 0;
for(var i in tickElem)
{
var grp = ticketString.slice(strPoint, (strPoint += 15));
grp.sort();
tickElem[i].innerText = grp.join('');
}
How about this compact version:
var start = 0;
['ticketOne', 'ticketTwo', 'ticketThree', 'ticketFour', 'ticketFive', 'ticketSix']
.forEach(function (id) {
document.getElementById(id).innerHTML = ticketString.slice(start, start += 15).sort();
});
<script>
var ticketString ="011722475204365360702637497481233455758302154058881928446789061241507324334876840738576186051132437816395663800818206590104559628214294664710935667287132130687703253151692742547985".match(/.{1,2}/g);
function loadTickets () {
var ticket = [];
for (var i = 0; i < ticketString.length / 15; i++) {
ticket[i] = ticketString.slice(i * 15, (i + 1) * 15).sort();
var tableCre = document.createElement("TABLE");
tableCre.setAttribute("id","ticketTable" + i)
document.body.appendChild(tableCre);
document.getElementById('ticketTable' + i).innerHTML = ticket[i];
}
console.log(ticketString);
console.log(ticket);
};
</script>
Got it fixed, #Quikers - given you the thumbs up; as this was the thinking I had - brought the .sort() function into the ticket[i] variable to trim it a little and based on the number of arrays, I have dynamically created 6 tables that hold the strings inside them. I have to develop the tables and set them up, but I am pretty much there now. Thanks for all your help.

Categories

Resources