ggt
ggt

Reputation: 331

How to compare every number in an array against each other? (javascript)

I have a set of numbers which are displayed like followed;

var data = "615:415,600:400,600:400,300:300"

Each number represents an x/y coordinate, and I would like to add a value next to each one which is calculated based on the frequency of the number within a range.

So, I would like to be able to compare each value against all others in this string, and from this perform the following functions;

  1. Remove the number from the string if it is a duplicate, and add :1
  2. If the x/y numbers are both within a range of 15 against any other number, add:1
  3. If there are no matches, add :0
  4. Turn into array

So using the data string, it would be transformed to;

var data = "615:415:1, 600:400:2, 300:300:0"

I have been trying to do this using a reducer function, but I'm struggling with mainly step 2. I'm hoping someone can help out?

Thanks - Code + Plunk below!


http://plnkr.co/edit/zPW1844cLnUFAlEI77jq?p=preview

var result = [];
var data = "615:415,600:400,600:400,300:300"
var count = 0;

var reducer = function(p, c, i, a) {

  if (p && p !== c) {

    var _t = p.split(":");
    result.push({
      x: _t[0],
      y: _t[1],
      value: count
    });

    count = 0;
    if (i === a.length - 1) {
      _t = c.split(":");
      result.push({
        x: _t[0],
        y: _t[1],
        value: count
      });
    }
  }
  else {
    count++;
  }
  return c
}

data.split(',').sort().reduce(reducer);

console.log(result)

Upvotes: 4

Views: 1339

Answers (3)

Redu
Redu

Reputation: 26191

All answers so far are good. I just would like to introduce a little variety by inventing an Array.prototype.withEachOther() method. Which just takes a callback an invokes the callback with each other item of the array being it's arguments as you may suggest. It works in place.

Array.prototype.withEachOther = function(cb){
                                  this.map(function(e,i,a){
                                             var t = a.slice();
                                             t.splice(0,i+1);
                                             t.map(function(f){
                                                     a[i] = cb(e,f);
                                                   });
                                           });
                                  return this;
                                };

var data = "615:415,600:400,600:400,300:300, 550 : 550".split(/\s*,\s*/)
                                                       .map(s => s.split(/\s*:\s*/).concat(0)),
      cb = (f,s) => (Math.abs(f[0]-s[0]) <= 15 && Math.abs(f[1]-s[1]) <= 15 && (f[2]++, s[2]++),f);
  result = data.reduceRight(function(p,c,i,a){
                              var fi = a.slice(0,i-a.length)
                                        .findIndex(f => f[0] === c[0] && f[1] === c[1]);
                              fi !== -1 ? (a[fi][2] += ++c[2], a.splice(i,1))
                                        : p.push(c);
                              return p;
                            },[])
               .withEachOther(cb)
               .reduce((p,c) => p += c[0]+":"+c[1]+":"+c[2]+", ","");
console.log(result);

Upvotes: 0

RobG
RobG

Reputation: 147453

Here's an alternative:

var data = "615:415,600:400,600:400,300:300";

var result = (function (s) {
  var result = {};
  var values = [];
  // Process each value
  s.split(',').forEach(function (v) {
    var b = v.split(':');
    // If a match, increment count by 2 (once for match and again for within 15)
    if (result[v]) {
      result[v].count += 2; 

    // Otherwise, just check for within 15
    } else {
      result[v] = {x:b[0], y:b[1], count:0};
      values.forEach(function(xy, i){
        if (xy[0]>= (b[0]-15) && xy[0] <= (+b[0]+15) &&
            xy[1]>= (b[1]-15) && xy[1] <= (+b[1]+15) ) {
            ++result[xy.join(':')].count;  // Increment for nearby only
        }
      })
      values.push([b[0],b[1]]);
    }
  })
  // Create required string format
  return Object.keys(result).reduce(function(arr, key){
    arr.push(key + ':' + result[key].count);
    return arr;
  },[]).join(', ');
})(data)

console.log(result);

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386720

You could use a step-by-step approach and split the string first in coordinates, generate a hash table for the coordinates with count and filter only unique coordinates.

Then compare each unique coordinates with each other and count if inside of a given range.

Later map the coordinates with the count and join to string.

var data = "615:415,600:400,600:400,300:300",
    result = function (array) {
        var i, j,
            hash = Object.create(null),
            unique = array.split(',').filter(function (a) {
                var parts = a.split(':');
                if (!hash[a]) {
                    hash[a] = [parts[0], parts[1], 0]; // [x, y, count]
                    return true;
                }
                hash[a][2]++;
            });

        for (i = 0; i < unique.length - 1; i++) {
            for (j = i + 1; j < unique.length; j++) {
                if (
                    Math.abs(hash[unique[i]][0] - hash[unique[j]][0]) <= 15 &&
                    Math.abs(hash[unique[i]][1] - hash[unique[j]][1]) <= 15
                ) {
                    hash[unique[i]][2]++;
                    hash[unique[j]][2]++;
                }
            }
        }
        return unique.map(function (a) {
            return hash[a].join(':');
        }).join(', ');
    }(data);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Related Questions