Как быстро масштабировать двумерный массив в javascript?
Для двухмерного массива a:
let a = [
[0, 0, 1, 0],
[0, 1, 1, 1],
[0, 0, 1, 0],
[0, 0, 1, 1]
]
Как я могу масштабировать данный коэффициент? Например, массив b представляет собой массив, масштабированный на 4:
let b =[
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]
]
Это код, который я написал для выполнения этой операции, но медленный (клиентский браузер: Chrome) при работе с большими массивами (200 x 200) и масштабированием позволяет сказать по 16-ти.
// scale an array by a factor of 'scale'
const scaledMatrixArray = (arr, scale) => {
let newArr = [];
arr.forEach((el) => {
let newArrRow = [];
el.forEach((el) => {
for (let j = 0; j < scale; j++) {
newArrRow.push(el);
}
});
for(let i = 0; i < scale ; i++) {
newArr.push(newArrRow);
}
});
return newArr;
};
Я понимаю, что моя реализация - это некоторый вариант O (n ^ 2) и очень неэффективна. Я ищу лучший способ сделать это или библиотеку, которая делает это лучше и быстрее. Мой конечный результат заключается в том, что мой массив NXN с более N> 200 может масштабироваться до массива 800 x 800 в наиболее эффективном, быстром и наименее запоминающем режиме.
Ответы
Ответ 1
Здесь очень сокращенный способ, используя Array().fill
, он работает быстрее, чем другие ответы, по крайней мере, в моем браузере.
Я добавил две версии: один с помощью оператора спредов, а другой - с помощью .apply
. Я получаю более быстрые результаты с apply
.
function scaleSpread(array, factor) {
const scaled = [];
for(const row of array) {
let x = [];
for(const item of row)
x.push(...Array(factor).fill(item));
scaled.push(...Array(factor).fill(x));
}
return scaled;
}
function scaleApply(array, factor) {
const scaled = [];
for(const row of array) {
let x = [];
for(const item of row)
x.push.apply(x, Array(factor).fill(item));
scaled.push.apply(scaled, Array(factor).fill(x));
}
return scaled;
}
function scaleConcat(array, factor) {
let scaled = [];
for(const row of array) {
let x = [];
for(const item of row)
x = x.concat(Array(factor).fill(item));
scaled = scaled.concat(Array(factor).fill(x));
}
return scaled;
}
var a = [ [0, 0, 1, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 1, 1] ]
console.time('spread');
scaleSpread(a, 10000);
console.timeEnd('spread');
console.time('apply');
scaleApply(a, 10000);
console.timeEnd('apply');
console.time('concat');
scaleConcat(a, 10000);
console.timeEnd('concat');
Ответ 2
Этот подход использует for loop
для итерации n-мерного массива в течение n
раз.
Это использует метод Array.splice, захватывая исходное значение и вставляя его в массив по определенному индексу.
PS: Исходный массив (который является a
), здесь мутируется. Но вы всегда можете клонировать исходный массив и создавать b
для результата, как вы хотели.
var a = [
[0, 0, 1, 0],
[0, 1, 1, 1],
[0, 0, 1, 0],
[0, 0, 1, 1]
],
scale = 4,
scaleTheArray = function (arrayToScale, nTimes) {
for (var idx = 0, i = 0, len = arrayToScale.length * nTimes; i < len; i++) {
var elem = arrayToScale[idx];
/* Insert the element into (idx + 1) */
arrayToScale.splice(idx + 1, 0, elem);
/* Add idx for the next elements */
if ((i + 1) % nTimes === 0) {
idx += nTimes + 1;
}
}
};
console.time('testScale');
/* 1. Expand each of the a[n] length */
for (var i = 0, len = a.length; i < len; i++) {
var arr = a[i];
scaleTheArray(arr, scale - 1);
}
/* 2. Expand each of the a length */
scaleTheArray(a, scale - 1);
console.timeEnd('testScale');
Ответ 3
В общем, меньше вызовов функций = меньше накладных расходов:
function scale1D(arr, n)
{
for (var i = arr.length *= n; i; )
arr[--i] = arr[i / n | 0]
}
function scale2D(arr, n)
{
for (var i = arr.length; i; )
scale1D(arr[--i], n)
scale1D(arr, n)
}
var a = [ [0, 0, 1, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 1, 1] ]
console.time( 1e5 )
scale2D(a, 1e5)
console.timeEnd( 1e5 )
var b = [ [0, 0, 1, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 1, 1] ]
scale2D(b, 4)
console.log( JSON.stringify( b ).replace(/],/g, '],\n ') )
Ответ 4
Немного веселья, вы можете сделать это лениво, если не получаете доступ ко многим значениям. Не тестировали этот код много, но должны работать
var a = [
[0, 0, 1, 0],
[0, 1, 1, 1],
[0, 0, 1, 0],
[0, 0, 1, 42]
],
scale = 4;
for (var idx = 0; idx < a.length; idx++) {
a[idx] = new Proxy(a[idx], {
get: function(target, i) {
return target[Math.floor(i/scale)];
}
});
}
a = new Proxy(a, {
get: function(target, i) {
return target[Math.floor(i/scale)];
}
});
console.log(a[16-1][16-1])
for (var ii = 0; ii < 16;ii++) {
for(var j=0;j<16;j++){
console.log(a[ii][j])
}
}