GROVER.
GROVER.

Reputation: 4388

skip every nth value in array based on value

The Problem

I'm developing a graph which utilises an array of samples to build itself. Unfortunately, the graph needs to be able to be resized, and therefore some of the samples must be removed from the graph.

To partly fix the issue, I'm using the modulus (%) operator to skip over every nth sample in the provided array based on the width of the canvas that it is being printed on. So, assuming we always have 1800 samples to work with, and the width of the canvas is 600px, the canvas will skip over every 3rd sample (1800 / 600 = 3).

The problem with this is that if the canvas has a width of, let's say 720px, the returned value will be a decimal (2.5 to be exact) and modulus doesn't exactly like this.


The Code

JavaScript:

var canvas = document.getElementById("cvs"),
    samples = [ ... ]; // assume there's 1800 samples here between 0-140

var x = 0,
    ctx = canvas.getContext("2d");

// iterate over each of the samples
for(var i = 0; i < samples.length; i++){
    // if there is no remainder when i / (1800 / 720)
    if((i % ((samples.length / canvas.width))) == 0){
        var height = samples[i] * ((canvas.height || 60) / 140);

        // create one bar with appropriate values
        ctx.fillStyle = "red";
        ctx.fillRect(
            x + (x * 2), // left
            (canvas.height || 60) - height, // top
            2, // width
            height // height
        );
        ctx.fill();

        x++;
    }   
}

HTML:

<canvas id="cvs" width="720" height="70"></canvas>

Essentially, as many samples as possible to fit within the canvas, by discarding ever nth sample based on the width of the canvas. It is probably some really easy mathematics, but this is all I could come up with thus far.

All help is appreciated, cheers.

Upvotes: 1

Views: 281

Answers (1)

Mark
Mark

Reputation: 92481

Instead of thinking of skipping entries in your original, think of taking entries from your original. For example if you had an array of fifty items and you want to downsample to 20 (that's the same 2.5 rate) you can take every 2.5th element rounded.

function resize(arr, new_size){
    let orig_size = arr.length
    return Array.from({length: new_size}, (_, i) => 
           arr[Math.floor(i * (orig_size + Math.floor(orig_size/new_size))/new_size)])
}

// take 20 from 50
let arr_size = 50
let arr = Array.from({length: arr_size}, (_, i) => i)
console.log(resize(arr, 20))

// take 19 from 29
arr_size = 29
arr = Array.from({length: arr_size}, (_, i) => i)
console.log(resize(arr, 19))

// take 17 from 200
arr_size = 200
arr = Array.from({length: arr_size}, (_, i) => i)
console.log(resize(arr, 17))

Notice that it doesn't take every 2nd or every 3rg, but alternates. This should give you reasonable results for any array and resize value. You could also do a for loop from 0 to new length instead of Array.from in the function with the same results.

EDIT: Using a for loop to push into a new array

function resize(arr, new_size){
    let orig_size = arr.length
    let smaller_array = []
    for (let i = 0; i<new_size; i++){
        let large_array_index = Math.floor(i * (orig_size + Math.floor(orig_size/new_size))/new_size)
        smaller_array[i] = arr[large_array_index]
    }
    return smaller_array
}

// take 20 from 50
let arr_size = 50
let arr = Array.from({length: arr_size}, (_, i) => i)
console.log(resize(arr, 20))

Upvotes: 3

Related Questions