UnderscoreJS按直方图的数值间隔组

时间:2017-01-11 20:40:01

标签: javascript arrays underscore.js

有没有办法将数字列表分组为带下划线的数字区间?

// Input:
var arr = [0,1,2,8];
var interval = 3;

// Output:
// There are 3 numbers from 0 to <3
// There are 0 numbers from 3 to <6
// There is 1 number from 6 to <9 
// Returns [3, 0, 1]

我注意到一些解决方案不会测试更大的值。尝试第二个测试用例:

var arr = [110,113,116,119];

4 个答案:

答案 0 :(得分:1)

在普通的Javascript中,您可以将数字除以间隔,并使用整数部分进行分组。

使用数组和缺少间隔。

&#13;
&#13;
function getHistogram(array, interval) {
    var bin,
        result = [];

    array.forEach(function (a) {
        var key = Math.floor(a / interval);
        if (!bin) {
            bin = [key, key];
            result[0] = 0;
        }
        while (key < bin[0]) {
            --bin[0];
            result.unshift(0);
        }
        while (key > bin[1]) {
            ++bin[1];
            result.push(0);
        }
        ++result[key - bin[0]];
    });
    return result;
}

console.log(getHistogram([0, 1, 2, 8], 3));
console.log(getHistogram([110, 113, 116, 119], 3));
console.log(getHistogram([15, 10, 26], 3));
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

使用对象和缺少间隔。

&#13;
&#13;
function getHistogram(array, interval) {
    var bin,
        result = {};

    array.forEach(function (a) {
        var key = Math.floor(a / interval);
        if (!bin) {
            bin = [key, key];
            result[key] = 0;
        }
        while (key < bin[0]) {
            result[--bin[0]] = 0;
        }
        while (key > bin[1]) {
            result[++bin[1]] = 0;
        }
        ++result[key];
    });
    return result;
}

console.log(getHistogram([0, 1, 2, 8], 3));
console.log(getHistogram([110, 113, 116, 119], 3));
console.log(getHistogram([15, 10, 26], 3));
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

答案 1 :(得分:0)

这个解决方案的想法是生成一系列直方图的X值,然后迭代数组,同时将小于每个X的值放在计数中。

var getIntervalCounts = function (arr){
  // Sort backwards to iterate through in reverse later
  arr.sort(function(a,b){
    return b - a;
  });

  var interval = 3;

  // Get values for range of histogram's x axis
  var greatestVal = arr[0];
  var leastVal = arr[arr.length-1];
  var x = _.range(leastVal + interval, greatestVal + interval + 1, interval);
  
  // Get value counts for histogram's y axis
  var y = _(x).map(function(num){
    var count = 0;

    // Remove elements from end of array while we iterate through
    // to avoid duplicate lookups
    for (var i = arr.length - 1; i >= 0; i--){
      // Put everything less than the histogram x in that x value
      if (arr[i] < num) {
        count++;
        arr.pop();
      } else {
        break;
      }
    }

    return count;
  });
  
  // console.log(x);
  console.log(y);
}

getIntervalCounts([0,1,2,8]);
getIntervalCounts([110,111,112,118]);
getIntervalCounts([110,111,112,118,119]);
getIntervalCounts([110,111,112,118,119,120]);
<script src="http://underscorejs.org/underscore-min.js"></script>

答案 2 :(得分:0)

这是我使用普通JS的方法。我已经为您的示例添加了更多数字以便更好地进行测试。

我使用新数组来存储每个间隔中出现的次数。第一个位置代表元素的数量&lt;间隔,第二个&lt; 2 *间隔......等等。

我保留对最后一个有效索引的引用,以便我可以用零填充空单元格。

更新:在0 <= x <= 3

范围内没有值时,避免将第一个数字设为undefined的小修补程序

&#13;
&#13;
// Input:
var arr = [ 110, 113, 116, 119 ],
  interval = 3,
  res = [],
  lastIdx = -1;

arr.forEach(function(el) {
  var intPart = Math.floor(el / interval),
    index = el && intPart * interval === el ? intPart - 1 : intPart;

  res[index] = (res[index] || 0) + 1;
  res.fill(0, lastIdx + 1, index);
  lastIdx = index;
});

console.log(res);
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
&#13;
&#13;
&#13;

更新2:使用下划线的版本。它使用countBy来获取间隔并避免使用Array.prototype.fill,因为它是ES6功能。

&#13;
&#13;
function getZeroFilledArr(len) {
  return Array.apply(null, Array(len)).map(Number.prototype.valueOf, 0);
}

function getIntervalLentgh(intervals) {
  return Number(_.max(intervals)) + 1;
}

var arr = [110, 113, 116, 119],
  interval = 3,
  intervals = _.countBy(arr, function(el) {
    var intPart = Math.floor(el / interval);
    return el && intPart * interval === el ? intPart - 1 : intPart;
  }),
  zeroFilledArr = getZeroFilledArr(getIntervalLentgh(_.keys(intervals)));


console.log(_.reduce(intervals, function(memo, value, key) {
  memo[key] = value;
  return memo;
}, zeroFilledArr));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
&#13;
&#13;
&#13;

答案 3 :(得分:0)

我认为这是最优雅的方式

login
_.mixin({
            step: function(ar, s) {
                var dummy = _.times(Math.ceil(_.max(ar)/s), function(){ return 0; });
                _.each(ar, function(x) {
                  dummy[Math.floor(x/s)]++; 
                });          
                return dummy;
            }
          });
        
        
          var arr = [0,1,2,4,6,7,8,9,22];
          var res1 =   _.step(arr, 3);                                
          console.log("RESULT1 ", res1.length, res1);

          var arr2 = [ 110, 113, 116, 119 ];
          var res2 = _.step(arr2, 3);
          console.log("RESULT2 ", res2.length, res2 );

以前的解决方案不考虑数组的最大值,因此不要创建结果数组的正确大小。

使用_.mixin可以为下划线定义一个可应用于数组的新函数。所以我创建了一个_.step函数,它接受数组和步骤。