2017-01-11 6 views
5

Есть ли способ группировать список чисел в числовые интервалы с подчеркиванием?UnderscoreJS group с помощью числовых интервалов для гистограммы

// 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]; 
+0

это данные отсортированные? –

ответ

1

Попросту Javascript, вы можете просто разделить число на интервале и использовать целую часть для группировки.

С массивом и отсутствующими интервалами.

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));
.as-console-wrapper { max-height: 100% !important; top: 0; }

С объектом и недостающими интервалами.

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));
.as-console-wrapper { max-height: 100% !important; top: 0; }

+0

Ваш код дает 0: 3 и 6: 1, но мне тоже нужно 3: 0. Я думаю, ты на правильном пути. –

+1

@JackWade, теперь в массиве с нулями по умолчанию. –

+0

Это сокращается для больших значений. вар обр = [ 110, 113, 116, ], интервал = 3, –

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>

+1

Это единственное правильное решение. Я надеялся на более элегантное решение стиля _.chain. –

+0

Правильно ... Я уверен, что есть способ, который немного более математичен, который мог бы просто вспахивать несортированный массив в O (n) времени. – y3sh

0

Это мой подход, используя простые JS. Я добавил несколько примеров на ваш пример, чтобы лучше проверить его.

Я использую новый массив для хранения количества вхождений в каждом интервале. Первая позиция будет представлять количество элементов < интервал, второй < 2 * интервал ... и так далее.

Я сохраняю ссылку на последний действительный индекс, чтобы заполнить пустые ячейки нулями.

ОБНОВЛЕНИЕ: Незначительное исправление, чтобы избежать необходимости первый номер в качестве undefined, когда нет никакого значения в диапазоне от 0 < = х < = 3

// 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);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

ОБНОВЛЕНИЕ 2: Версия с использованием подчеркивания. Он использует countBy для получения интервалов и избегает использования Array.prototype.fill, потому что это функция ES6.

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));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

+0

не используемое подчеркивание в соответствии с запросами – hpfs

+0

Попробуйте var arr = [ 110, 113, 116, ]; –

0

Я думаю, что это самый элегантный способ Doit

_.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);
<script src="http://underscorejs.org/underscore-min.js"></script>

Предыдущие решения не учитывают максимальное значение массива так не создавайте правильный размер массива результатов.

Использование _.mixin дает вам возможность определить новую функцию для подчеркивания, которая может применяться к массиву. Поэтому я создал функцию _.step, которая принимает массив и шаг.

+0

Это перерывы для var arr = [ 110, 113, 116, ]; –

+0

Почему вы говорите это? Он возвращает вам массив из 40, 36, 0, а последние четыре - 1. – hpfs

+0

сделал исправление для незначительной ошибки в mixin referin вместо arr вместо ar – hpfs