I want a function (collisions
) that can remove object from an array when they are too similar.
I have a working version, but it is so ugly that I don't want to commit it.
Setup
In my 10x10 pool I have a lot of boats:
var boats = [
{name: "A", position: [1,1] }, // collides with E and G
{name: "B", position: [7,8] }, // collides with D
{name: "C", position: [8,2] }, // will not collide
{name: "D", position: [7,9] }, // collides with B
{name: "E", position: [2,1] }, // collides with A and G
{name: "F", position: [1,7] }, // will not collide
{name: "G", position: [2,2] }, // collides with A and E
]
Ships need to be carefull not to get to close to other boats.
const collisionDistance = 5;
function distance(boat1, boat2) {
return Math.sqrt(
Math.pow(boat1.position[0] - boat2.position[0], 2)
Math.pow(boat1.position[1] - boat2.position[1], 2)
);
}
If they fail to do so both boats will sink. (this is the part that needs refactoring)
// Boats that are too close to another boat are removed from the list of boats
// How can I make this beautiful?
function collisions() {
var collidingBoatIndices = new Set();
// iterate over pairs of the list
for (let i = 0; i < boats.length; i ) {
for (let j = i 1; j < boats.length; j ) {
if (distance(boats[i], boats[j]) < collisionDistance) {
collidingBoatIndices.add(i);
collidingBoatIndices.add(j);
}
}
}
// delete from biggest to smallest index so there is no shift in the elements
for (let index of Array.from(collidingBoatIndices).sort().reverse()){
console.log("Boat sank: ", boats[index])
boats.splice(index, 1);
}
}
In the setup above I expect only boats C
and F
to survive.
console.log("Boats at start:", boats.map((boat => boat.name)));
collisions()
console.log("Boats left over:", boats.map((boat => boat.name)));
Question
So my question is: How can I make the function collisions
simpler and more readable?
CodePudding user response:
I don't get it why you are saving the indexes instead of the boats... check this:
var boats = [
{name: "A", position: [1,1] }, // collides with E and G
{name: "B", position: [7,8] }, // collides with D
{name: "C", position: [8,2] }, // will not collide
{name: "D", position: [7,9] }, // collides with B
{name: "E", position: [2,1] }, // collides with A and G
{name: "F", position: [1,7] }, // will not collide
{name: "G", position: [2,2] }, // collides with A and E
]
const collisionDistance = 5;
function distance(boat1, boat2) {
return Math.sqrt(
Math.pow(boat1.position[0] - boat2.position[0], 2)
Math.pow(boat1.position[1] - boat2.position[1], 2)
);
}
const res = []
for(let i = 0; i < boats.length ; i ){
let flag = true;
for(let j = 0; j < boats.length; j ){
if(distance(boats[i], boats[j]) < collisionDistance && i != j){
flag = false;
}
}
if(flag){
res.push(boats[i])
}
}
console.log(
res
)
But you can take a way more readable functional way like this:
var boats = [
{name: "A", position: [1,1] }, // collides with E and G
{name: "B", position: [7,8] }, // collides with D
{name: "C", position: [8,2] }, // will not collide
{name: "D", position: [7,9] }, // collides with B
{name: "E", position: [2,1] }, // collides with A and G
{name: "F", position: [1,7] }, // will not collide
{name: "G", position: [2,2] }, // collides with A and E
]
const collisionDistance = 5;
function distance(boat1, boat2) {
return Math.sqrt(
Math.pow(boat1.position[0] - boat2.position[0], 2)
Math.pow(boat1.position[1] - boat2.position[1], 2)
);
}
const res = boats.filter(
(b1, i) => boats.every(
(b2, j) => !(distance(b1, b2) < collisionDistance && i != j)
)
)
console.log(res)
Final version
As @pilchard pointed out, you can increase the performance by using some
(even though in a 10x10 sheet you won't see such a improvement):
var boats = [
{name: "A", position: [1,1] }, // collides with E and G
{name: "B", position: [7,8] }, // collides with D
{name: "C", position: [8,2] }, // will not collide
{name: "D", position: [7,9] }, // collides with B
{name: "E", position: [2,1] }, // collides with A and G
{name: "F", position: [1,7] }, // will not collide
{name: "G", position: [2,2] }, // collides with A and E
]
const collisionDistance = 5;
function distance(boat1, boat2) {
return Math.sqrt(
Math.pow(boat1.position[0] - boat2.position[0], 2)
Math.pow(boat1.position[1] - boat2.position[1], 2)
);
}
const res = boats.filter(
(b1, i) => !boats.some(
(b2, j) => distance(b1, b2) < collisionDistance && i != j
)
)
console.log(res)