Home > Mobile >  Pairwise remove elements from array
Pairwise remove elements from array

Time:10-14

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)

  • Related