2

I have following two arrays:

var element_1 = new Array([x1,y1],[x2,y2],[x3,y3],[x4,y4]);
var element_2 = new Array([x1,y1],[x2,y2],[x3,y3],[x4,y4]);

Logic: I want to run a loop (nested) where each element of element_1 (for eg [x1,y1]) is compared to each element of element_2 and the shortest distance between them shall be calculated within the loop (I know how to calculate the shortest path). The tricky part here is that I need a reference that which pair made the shortest past and then obtain those [x1,y1] and [x2,y2] combinations to draw a line.

Sample data:

var element_1 = new Array([10,0],[20,10],[10,20],[0,10]);
var element_2 = new Array([10,30],[20,40],[10,50],[0,40]);

Line should be made between [10,20] and [10,30]. Also, I would somehow need to store the coordinates somewhere to pass it to the line drawing function

How can I do this? Any leads would be highly appreciated.

Arihant
  • 3,349
  • 13
  • 42
  • 77
  • Can you provide a set of actual coordinates, and the result that you expect? – blex Jun 09 '16 at 20:31
  • @blex the coordinates are stored in the array dynamically onclick when an element is clicked. the 4 [x,y] pair are coordinates of the anchor points of a rectangle (mid points of height and width). Thus 2 array have anchors or 2 elements. The goal is to thus find the shortest connecting anchors and their x,y values to draw a line between them – Arihant Jun 09 '16 at 20:35
  • @Redu it was a typing error. corrected it. thanks! – Arihant Jun 09 '16 at 20:36
  • Rectangles? Why than don't you store simply `el=[x,y,w,h]` ... – Roko C. Buljan Jun 09 '16 at 20:40
  • @RokoC.Buljan that is because I need the incoming lines to join only certain point on the rectangle. Imagine UML diagrams – Arihant Jun 09 '16 at 20:41
  • @Arihant, can you add the examples with "real"(hypothetic) data and show the expected result? – RomanPerekhrest Jun 09 '16 at 20:43
  • @Arihant :) OK but if one point is at `x` than logically the other one is at `x+w` ... njeeh never mind. – Roko C. Buljan Jun 09 '16 at 20:45
  • @RomanPerekhrest added some sample data – Arihant Jun 09 '16 at 20:59

2 Answers2

3

Here is how I would do it:

var element_1 = [[0,0],[1,2],[5,3],[6,8]];
var element_2 = [[0,1],[1,4],[5,9],[9,8]];

var closest = {a: false, b: false, distance: false};

for(var i=0; i<element_1.length; i++) {
  for(var j=0; j<element_2.length; j++) {
    var distance = calculate_distance(element_1[i], element_2[j]);
    console.log('Distance between element_1['+i+'] and element_2['+j+']: ' + distance);
    if(closest.distance === false || distance < closest.distance) {
      closest = {a: element_1[i], b: element_2[j], distance: distance};
    }
  }
}

console.log('The shortest path is between '+closest.a+' and '+closest.b+', which is '+closest.distance);

function calculate_distance(a, b) {
  var width  = Math.abs( a[0] - b[0] ),
      height = Math.abs( a[1] - b[1] ),
      hypothenuse = Math.sqrt( width*width + height*height );
  return hypothenuse;
}

As Roko C. Buljan said, in your case you can just replace new Array() with []. Here's why.

Community
  • 1
  • 1
blex
  • 22,377
  • 5
  • 35
  • 65
1

Well i liked this question a lot. It inspired me to invent a generic Array method to apply a callback with each other items of two arrays. So i called it Array.prototype.withEachOther(). What it does is exactly what @blex has done in his solution with nested for loops. It applies an operation (provided by the callback) to each array item with the other array's item. Let's see how it works.

Array.prototype.withEachOther = function(a,cb,s=0){
  return this.reduce((p,et) => a.reduce((q,ea) => cb(et,ea,q),p),s);
};
var element_1 = [[10,0],[20,10],[10,20],[0,10]],
    element_2 = [[10,30],[20,40],[10,50],[0,40]],
           cb = (p1,p2,q) => {var h = Math.hypot(p1[0]-p2[0],p1[1]-p2[1]);
                              return h < q.d ? {d:h,p1:p1,p2:p2} : q},
      minDist = element_1.withEachOther(element_2,cb,{d:Number.MAX_SAFE_INTEGER,p1:[],p2:[]});
console.log(minDist);

So let's explain what's going on.

Array.prototype.withEachOther = function(a,cb,s=0){
  return this.reduce((p,et) => a.reduce((q,ea) => cb(et,ea,q),p),s);
};

is a reusable function. It will execute the operation that is provided in a callback function, with each other element of the two arrays. It takes 3 arguments (a,cb,s=0).

  • a is the second array that we will apply our callback to each item for each item of the array that is invoking .withEachOther.
  • cb is the callback. Below I will explain the callback applied specific for this problem .
  • s=0 is the initial (with a default value of 0) value that we will start with. It can be anything depending on the callback function.

return this.reduce((p,et) => a.reduce((q,ea) => cb(et,ea,q),p),s);

this part is the core of the function. As you see it has two nested reduces. The outer reduce has an initial value designated by the s, which is provided as explained above. The initial value gets initially assigned to the p argument of the outer reduce's callback and the other argument et is assigned one by one with each of the items of invoking array. (element of this). In the outer reduce we invoke another reduce (the inner reduce). The inner reduce starts with the initial value of the result of previous loop which is the p of outer reduce and after each calculation returns the result to it's reduced value variable q. q is our memory and tested in the callback to see if we keep it as it is or replace it with the result of our calculation. After inner reduce finishes a complete round it will return the q to p and the same mechanism will run again until we finish with all items of the array that's invoking .withEachOther.

cb = (p1,p2,q) => {var h = Math.hypot(p1[0]-p2[0],p1[1]-p2[1]);
                   return h < q.d ? {d:h,p1:p1,p2:p2} : q}

The callback is special to this problem. It will receive two points (each with x and y coordinates) Will calculate the distance between them and will compare it with the previously made calculation. If it's smaller it will replace q by returning this new value; if not it will return q as it is.

Redu
  • 19,106
  • 4
  • 44
  • 59