40

I get an Array with an unknown Number of data. But I only have an predefined amount of data to be shown/store. How can I take every nth Element of the initial Array and reduce it in JavaScript?

Eg.: I get an Array with size=10000, but are only able to show n=2k Elements.

I tried it like that: delta= Math.round(10*n/size)/10 = 0.2 -> take every 5th Element of the initial Array.

for (i = 0; i < oldArr.length; i++) {
  arr[i] = oldArr[i].filter(function (value, index, ar) {
    if (index % delta != 0) return false;
    return true;
  });
}

With 0.2 it´s always 0, but with some other deltas (0.3) it is working. Same for delta=0.4, i works, but every second Element is taken with that. What can I do to get this to work?

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
EsoMoa
  • 687
  • 1
  • 8
  • 19
  • What is `n`? What is `k`? What is `delta`? What is `oldArr`? – Oriol Nov 02 '15 at 16:57
  • How about `delta = size / n`? – Felix Kling Nov 02 '15 at 16:58
  • 0.2 evenly divides all the integers, so `someInt % 0.2 == 0` always. I think you want `someInt % (1 / 0.2)`, ie `someInt % 5` – James Nov 02 '15 at 17:01
  • 1
    @James `1 % 0.2` produces `0.19999999999999996` for me, because [floating point math is broken](http://stackoverflow.com/q/588004/1529630) – Oriol Nov 02 '15 at 17:06
  • @Oriol you're right! `1 % 0.25` works (because 0.25 can be exactly represented in binary, I suppose), I guess using modulus operator on non-integer operands is a bad idea in JS. – James Nov 02 '15 at 17:10
  • Oriol, I wrote in my starting Post what those Variables are. @FelixKling, lol that sounds logical. – EsoMoa Nov 02 '15 at 19:17

6 Answers6

63

Maybe one solution :

avoid filter because you don't want to loop over 10 000 elements ! just access them directly with a for loop !

 
var log = function(val){document.body.innerHTML+='<div></pre>'+val+'</pre></div>'} 

var oldArr = [0,1,2,3,4,5,6,7,8,9,10]
var arr = [];

var maxVal = 5;

var delta = Math.floor( oldArr.length / maxVal );

// avoid filter because you don't want
// to loop over 10000 elements !
// just access them directly with a for loop !
//                                 |
//                                 V
for (i = 0; i < oldArr.length; i=i+delta) {
  arr.push(oldArr[i]);
}


log('delta : ' + delta + ' length = ' + oldArr.length) ;
log(arr);
Anonymous0day
  • 3,012
  • 1
  • 14
  • 16
  • 1
    Thanks, I guess that´s it. Especially since that should be faster than with the modulo-check. And I don´´t have 10k Elements. It´s an 2D-Array, so in the other Version there would even be 100k checks. – EsoMoa Nov 02 '15 at 19:27
32

Filter itself returns an array. If I'm understanding you correctly, you don't need that surrounding loop. So:

newArr = oldArr.filter(function(value, index, Arr) {
    return index % 3 == 0;
});

will set newArr to every third value in oldArr.

nicholas
  • 14,184
  • 22
  • 82
  • 138
12

Try

arr = oldArr.filter(function (value, index, ar) {
    return (index % ratio == 0);
} );

where ratio is 2 if you want arr to be 1/2 of oldArr, 3 if you want it to be 1/3 of oldArr and so on.

ratio = Math.ceil(oldArr.length / size); // size in the new `arr` size

You were calling filter() on each element of oldAdd inside a loop and you're supposed to call filter() on the whole array to get a new filtered array back.

ericbn
  • 10,163
  • 3
  • 47
  • 55
0

Borrowing from @anonomyous0day's solution, generate a new Array with the desired indices from the given array:

(Take every 3 items)

Array.prototype.take = function(n) {
  if (!Number(n) && n !== 0) {
    throw new TypeError(`Array.take requires passing in a number.  Passed in ${typeof n}`);
  } else if (n <= 0) {
    throw new RangeError(`Array.take requires a number greater than 0.  Passed in ${n}`);
  }

  const selectedIndicesLength = Math.floor(this.length / n);
  return [...Array(selectedIndicesLength)].map((item, index) => this[index * n + 1]);
};

[1, 2, 3, 4, 5, 6, 7, 8].take(2); // => 2, 4, 6, 8
user1429980
  • 6,872
  • 2
  • 43
  • 53
0

this also works by using map to create the new array without iterating over all elements in the old array..

// create array with 10k entries
const oldArr = [ ...Array( 10000 ) ].map( ( _el, i ) => i );
const max = 10;
const delta = Math.floor( oldArr.length / max );

const newArr = [ ...Array( max ) ].map( ( _el, i ) => (
  oldArr[ i * delta ]
) );

console.log( newArr );
shunryu111
  • 5,895
  • 4
  • 27
  • 16
0

may help!

 const myFunction = (a, n) => {

   let array = []


    for(i = n; i <= a.length; i += n){
      array.push(a[i-1]);

   }

  return array;

}
Adam Foody
  • 19
  • 3