How to write out n*n Matrix values in JavaScript? - javascript

I'm new to JavaScript, so I decided to try to make a simple n*n Array. When I'm trying to write it out, I get, something starnge back.
var map = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 3, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 3, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
];
function GoTh() {
for (var i = 0; i < map.length; i++) {
for (var j = 0; j < map[i].length; j++) {
console.log(map[i][j]);
}
}
}
GoTh();
I expected it to write all 100 values, but it gives back this:
1 2 3 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 3 1

That's because same-value console.logs are grouped together in browsers' consoles.
This can usually be disabled, i.e. in Chrome & Opera:
In Firefox it's in console's settings (press F1 while using console)
Another option is to write more data along with the value, eg.:
console.log(i, j, map[i][j]);

The problem is the ouput in the console itself. If there are multiple similar entries - like the first 10 elements of value 1 in the first row plus the 1 in the second row, it will be grouped as a single entry of 11. You can see that there are multiple if you look to the right of a particular entry.

Related

Unable to update matrix created with Javascript Array constructor [duplicate]

This question already has answers here:
Unexpected behavior using Array Map on an Array Initialized with Array Fill [duplicate]
(1 answer)
Array.prototype.fill() with object passes reference and not new instance
(7 answers)
Array.fill(Array) creates copies by references not by value [duplicate]
(3 answers)
Closed 9 months ago.
I am doing some Javascript Matrix exercises and came across the problem of my Matrix not updating as expected when it's created using the Javascript Array constructor.
I created a matrix called theatre:
const theatre = new Array(5).fill(new Array(10).fill(0));
which prints as expected:
console.log(theatre)
[ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ]
however, when I attempt to change the values of each element in the first two rows to 1, every value in the matrix is changed.
for(let i = 0; i < 2; i++){
for(let j = 0; j < 10; j++){
theatre[i][j] = 1;
}
}
console.log(theatre)
[ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ]
To test further, I updated theatre to be equal to a matrix instead of using the array constructor and ran the for loop again which returned the expected output.
const theatre = [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ]
for(let i = 0; i < 2; i++){
for(let j = 0; j < 10; j++){
theatre[i][j] = 1;
}
}
console.log(theatre)
[ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ]
Why doesn't this logic work as expected on the matrix created with the Array constructor? Am I misunderstanding how the array constructor works with fill?

Text File to JavaScript Nested Array

I have this .txt file to change to a nested array:
000011000000
000100001100
000001100001
010010001000
100101000100
101010010001
001000001001
000001000111
010100100010
010010010010
000000011100
001001110000
to the format:
var data = [
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
[1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1],
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1],
[0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0]
]
what I have done so far is:
Read the file:
function readMatrixFile(){
var inputElement = document.getElementById("adjencyMatrixInput");
var fileList = inputElement.files;
var plainMatrix = new FileReader();
plainMatrix.readAsText(fileList[0]);
plainMatrix.onload = function () {
//Add to Matrix
renderMatrix(plainMatrix)
}
}
and
2. Split the File
function renderMatrix(plainMatrix) {
var matrix = plainMatrix.result;
var mtx = [];
matrix = matrix.split("\n");
}
I know I need to push through a for loop, but not sure how to get the nested array.
Split the string by a newline, then map over each item and convert the string into an array of characters with spread syntax.
const str = `000011000000
000100001100
000001100001
010010001000
100101000100
101010010001
001000001001
000001000111
010100100010
010010010010
000000011100
001001110000`
const res = str.split("\n").map(e => [...e])
console.log(res)
To convert the characters to numbers, map over the array of characters and parse each item:
const str = `000011000000
000100001100
000001100001
010010001000
100101000100
101010010001
001000001001
000001000111
010100100010
010010010010
000000011100
001001110000`
const res = str.split("\n").map(e => [...e].map(e => +e))
console.log(res)
You turn matrix into a a string of ones and zeros, you just need to split the string and convert them to numbers
function renderMatrix(plainMatrix) {
var matrix = plainMatrix.result;
var mtx = matrix.split("\n"); // mtx is now an array of strings of ones and zeros
mtx = mtx.map(string => string.split('')); // mtx is now an array of arrays of number string
mtx = mtx.map(nested => nested.map(numberString => Number(numberString))); // mtx is now what you want
}

How to fill an 2D array with a 2D Smaller array in JavaScript

I apologize if this is not well explained. I am quite new to JavaScript.
I have a 2D arrayA that is 10x10 and a 2D arrayB that is 5x8. The smaller arrayB is populated with data and the larger arrayA is just populated with 0's by default.
How can i move the data from arrayB to arrayA while still leaving the leftover space of arrayA as 0's?
The end result must be that arrayA should contain all of the data in the same order as arrayB but with the leftover space still just containing 0's.
Assuming I understood the question correctly, you can loop over each value of arrayB and assign it at the same indexes in arrayA:
const arrayA = Array(10).fill(0).map(_ => Array(10).fill(0))
const arrayB = Array(5).fill(0).map(_ => Array(8).fill(1))
for (let y = 0; y < arrayB.length; y++) {
for (let x = 0; x < arrayB[y].length; x++) {
arrayA[y][x] = arrayB[y][x]
}
}
console.log(arrayA.map(v => v.join(', ')).join('\n'))
The console.log is just for readability, to understand how the matrix looks.
A simple Array.map() will do the job:
const arrA = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];
const arrB = [
[0,1,2,3,4],
[0,1,2,3,4],
[0,1,2,3,4],
[0,1,2,3,4],
[0,1,2,3,4],
[0,1,2,3,4],
[0,1,2,3,4],
[0,1,2,3,4],
];
const B2A = () => arrA.map(
(val, index) => val.map(
(subVal, subIndex) => {
if(arrB[index] && arrB[index][subIndex]) return arrB[index][subIndex]
return subVal;
})
)
console.log(B2A());
Use Array.from and iterate, while iterate check if the value exists in filler array. If exists use that value otherwise same array value.
const fill = (arr1, arr2) =>
Array.from(arr1, (arr, row) =>
Array.from(arr, (value, col) => (arr2[row] && arr2[row][col]) || value)
);
const arrA = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
const arrB = [
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4]
];
console.log(JSON.stringify(fill(arrA, arrB)));

How to determine if chunks of a value could fit into an array

Given an input array of 1s and 0s of arbitrary length, such as:
[0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0]
How can I (most efficiently) calculate a new array detailing if chunks of size n 0s which can fit into the input?
Examples
Where output now means
1 == 'Yes a zero chunk that size could go here'
0 == 'Couldn't fit a chunk that size there'
Chunk size = 1 ([0]): [1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1]
Chunk size = 2 ([0,0]): [0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1]
Chunk size = 3 ([0,0,0]): [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0]
Chunk size = 4 ([0,0,0,0]): [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
I'm using ES6, so any language features are fine.
EDIT:
The output shouldn't just be a 'yes'/'no' a chunk of size 'n' can fit in this array. More specifically, it needs to be an array of the same length, where a '1' / 'true' array value represents either:
Yes, a chunk of size 'n' could start and fit here, or
Yes, this slot could contain a chunk of size 'n' that started before it
On that second point, this would mean for chunk size 3:
input = [1, 0, 0, 0, 0, 1];
output = [0, 1, 1, 1, 1, 0];
Edit 2:
This is the function I came up with but it seems very inefficient:
const calculateOutput = (input, chunkSize) => {
const output = input.map((value, index) => {
let chunkFitsHere = false;
const start = (index - (chunkSize) >= 0) ? index - (chunkSize) : 0;
const possibleValues = input.slice(start, index + chunkSize);
possibleValues.forEach((pValue, pIndex) => {
let consecutives = 0;
for (let i = 0; i < possibleValues.length - 1; i += 1) {
if (consecutives === chunkSize) {
break;
}
if (possibleValues[i+1] === 0) {
consecutives += 1;
} else {
consecutives = 0;
}
}
if (consecutives === chunkSize) {
chunkFitsHere = true;
}
});
return chunkFitsHere ? 1 : 0;
});
return output;
};
You could count the connected free places by reversing the array and take an flag for the last return value for mapping.
Array before final mapping and after, depending on n
1 0 4 3 2 1 0 0 2 1 0 0 0 3 2 1 0 2 1 array with counter
1 0 4 4 4 4 0 0 2 2 0 0 0 3 3 3 0 2 2 array same counter
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ------------------
1 0 1 1 1 1 0 0 1 1 0 0 0 1 1 1 0 1 1 n = 1
0 0 1 1 1 1 0 0 1 1 0 0 0 1 1 1 0 1 1 n = 2
0 0 1 1 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0 n = 3
0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 n = 4
function chunk(array, n) {
return array
.slice()
.reverse()
.map((c => v => v ? c = 0 : ++c)(0))
.reverse()
.map((l => v => l = v && (l || v))(0))
.map(v => +(v >= n));
}
var array = [0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0];
console.log(chunk(array, 1).join(' '));
console.log(chunk(array, 2).join(' '));
console.log(chunk(array, 3).join(' '));
console.log(chunk(array, 4).join(' '));
If you like only one mapping at the end, remove the last two map and use
.map((l => v => l = +(v && (v >= n || l)))(0));
for final mapping.
You can traverse array once, calculating length of the series of zeros. If it is long enough, fill output with series of 1 of the same length.
Note that you can fill output for different chunk lengths simultaneously (filling chunks in rows of 2d array with row index not exceeding zerolen)
Python code:
def zerochunks(a, n):
l = len(a)
result = [0] * l #list of l zeros
zerolen = 0
for i in range(l + 1):
### Short circuit evaluation to shorten code
if (i==l) or (a[i] != 0):
if (zerolen >= n): #series of zeros is finished here
for j in range(i - zerolen, i):
result[j] = 1
zerolen = 0
else:
zerolen += 1
return result
print(zerochunks([0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0], 1))
print(zerochunks([0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0], 2))
print(zerochunks([0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0], 3))
print(zerochunks([0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0], 4))
print(zerochunks([0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0], 5))
>>>
[1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1]
[0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1]
[0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0]
[0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
And function for getting all arrays with chunks in maxn range:
def zerochunksall(a, maxn):
l = len(a)
result = [[0] * l for i in range(maxn)]
zerolen = 0
for i in range(l + 1):
if (i==l) or (a[i] != 0):
for k in range(0, zerolen):
for j in range(i - zerolen, i):
result[k][j] = 1
zerolen = 0
else:
zerolen += 1
return result
print(zerochunksall([0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0], 5))
>>
[[1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1],
[0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1],
[0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
function fit(input, n) {
var output = [];
for(var i = 0; i < input.length; i++) {
var fit = true;
for(var j = i; j < i + n; j++) {
if(j >= input.length || input[j]) { // either over the array size or occupied
fit = false;
break;
}
}
output.push(fit);
}
return output;
}
var input = [0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0];
console.log(fit(input, 1).map(v => +v));
console.log(fit(input, 2).map(v => +v));
console.log(fit(input, 3).map(v => +v));
console.log(fit(input, 4).map(v => +v));
outputs
[ 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1 ]
[ 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 ]
[ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 ]
[ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
These don't seem to exactly match your expected output though, but that may be because I'm assuming the flags in the array should mark the start of the chunk (i.e. can you fit N truthy values in the array starting from this position).
(See below, where e = your expected result, n = the output of my algorithm.)
input = [ 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0 ]
e = 2 = [ 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1 ]
n = 2 = [ 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 ]
==== ==== ==== ====
You can use array.prototype.some to check if the chunk can fit starting to some index of the input array. To check if the chunk with it's actual length can fit, you can use array.prototype.every:
var input = [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0];
var chunk = [0,0,0];
var res = input.some((e, i) => chunk.every((c, j) => input[i + j] === c));
console.log(res);
var input = [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0];
var chunk = [0, 0, 0, 0, 0, 0, 0, 0];
var res = input.some((e, i) => chunk.every((c, j) => input[i + j] === c));
console.log(res);
You could use some method and then if the current element is 0 you can slice part of the array from current index and check if its all zeros.
const data = [0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0]
function check(arr, n) {
let chunk = Array(n).fill(0).join('');
return arr.some((e, i) => e === 0 && arr.slice(i, i + n).join('') == chunk)
}
console.log(check(data, 3))
console.log(check(data, 4))
console.log(check(data, 5))

Multidimensional array fill

I'm trying to fill an area in a multidimensional array and not sure on the approach.
For example I have the following array:
var map = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 2, 2, 2, 2, 2, 0, 0],
[0, 2, 0, 0, 0, 0, 2, 0, 0],
[0, 2, 0, 2, 0, 0, 2, 0, 0],
[0, 2, 0, 0, 2, 0, 2, 0, 0],
[0, 0, 2, 0, 0, 0, 2, 0, 0],
[0, 0, 0, 2, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
];
And then I am trying to get the number from X and Y position and fill all those numbers (which is 0) with a number given such as 1, which will result in the following array:
var map = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 2, 2, 2, 2, 2, 0, 0],
[0, 2, 1, 1, 1, 1, 2, 0, 0],
[0, 2, 1, 2, 1, 1, 2, 0, 0],
[0, 2, 1, 1, 2, 1, 2, 0, 0],
[0, 0, 2, 1, 1, 1, 2, 0, 0],
[0, 0, 0, 2, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
];
Basically just replacing all numbers next to each other (0) with (1) within that area.
What is the correct way to do this with JavaScript?
Assuming you're given a starting position and you want to then fill all neighboring values up/down, left/right that contain the same value, you can do something like this:
var map = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 2, 2, 2, 2, 2, 0, 0],
[0, 2, 0, 0, 0, 0, 2, 0, 0],
[0, 2, 0, 2, 0, 0, 2, 0, 0],
[0, 2, 0, 0, 2, 0, 2, 0, 0],
[0, 0, 2, 0, 0, 0, 2, 0, 0],
[0, 0, 0, 2, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
];
function fill(data, x, y, newValue) {
// get target value
var target = data[x][y];
function flow(x,y) {
// bounds check what we were passed
if (x >= 0 && x < data.length && y >= 0 && y < data[x].length) {
if (data[x][y] === target) {
data[x][y] = newValue;
flow(x-1, y); // check up
flow(x+1, y); // check down
flow(x, y-1); // check left
flow(x, y+1); // check right
}
}
}
flow(x,y);
}
fill(map, 2, 2, 1);
Working demo: http://jsfiddle.net/jfriend00/C83AT/
Here's a version that doesn't use recursion and appears to work with large data sets. Your large test data set wasn't a very interesting test pattern so I wouldn't say this is tested conclusively, but it seems to work on both the small and large data set:
Large data example: http://jsfiddle.net/jfriend00/8mrhN/
Small data example: http://jsfiddle.net/jfriend00/BFTub/ (easier to see the result)
function fill(data, x, y, newValue) {
// get target value
var target = data[x][y];
// maintain list of cells to process
// put the starting cell in the queue
var queue = [{x:x, y:y}], item;
while (queue.length) {
item = queue.shift();
x = item.x;
y = item.y;
if (data[x][y] === target) {
data[x][y] = newValue;
// up
if (x > 0) {
queue.push({x:x-1, y:y})
}
// down
if (x + 1 < data.length) {
queue.push({x:x+1, y:y})
}
// left
if (y > 0) {
queue.push({x:x, y:y-1});
}
// right
if (y + 1 < data[x].length) {
queue.push({x:x, y:y+1});
}
}
}
}
This could be optimized further for performance by testing the value before putting it in the queue and by following a given direction until you find a non-matching value, if required.
This is an alternative implementation (queue-based) roughly translated, no optimisations performed. There are also others.
Javascript
var map = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 2, 2, 2, 2, 2, 0, 0],
[0, 2, 0, 0, 0, 0, 2, 0, 0],
[0, 2, 0, 2, 0, 0, 2, 0, 0],
[0, 2, 0, 0, 2, 0, 2, 0, 0],
[0, 0, 2, 0, 0, 0, 2, 0, 0],
[0, 0, 0, 2, 2, 2, 2, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
];
/*
1. Set Q to the empty queue.
2. If the color of node is not equal to target-color, return.
3. Add node to Q.
4. For each element N of Q:
5. If the color of N is equal to target-color:
6. Set w and e equal to N.
7. Move w to the west until the color of the node to the west of w no longer matches target-color.
8. Move e to the east until the color of the node to the east of e no longer matches target-color.
9. For each node n between w and e:
10. Set the color of n to replacement-color.
11. If the color of the node to the north of n is target-color, add that node to Q.
12. If the color of the node to the south of n is target-color, add that node to Q.
13. Continue looping until Q is exhausted.
14. Return.
*/
function floodFill(data, node, targetValue, replacementValue) {
var Q;
if (data[node[0]][node[1]] === targetValue) {
Q = [node];
while (Q.length) {
var N = Q.shift(),
value,
index,
n,
e,
s,
w;
if (data.hasOwnProperty([N[0]]) && data[N[0]][N[1]] === targetValue) {
w = e = N[0];
do {
w -= 1;
} while (data.hasOwnProperty(w) && data[w][N[1]] === targetValue);
do {
e += 1;
} while (data.hasOwnProperty(e) && data[e][N[1]] === targetValue);
n = N[1] - 1;
s = N[1] + 1;
for (index = w + 1; index < e; index += 1) {
data[index][N[1]] = replacementValue;
if (data[index].hasOwnProperty(n) && data[index][n] === targetValue) {
Q.push([index, n]);
}
if (data[index].hasOwnProperty(s) && data[index][s] === targetValue) {
Q.push([index, s]);
}
}
}
}
}
}
floodFill(map, [2, 2], 0, 1);
map.forEach(function (m) {
console.log(JSON.stringify(m));
});
Output
[0,0,0,0,0,0,0,0,0]
[0,2,2,2,2,2,2,0,0]
[0,2,1,1,1,1,2,0,0]
[0,2,1,2,1,1,2,0,0]
[0,2,1,1,2,1,2,0,0]
[0,0,2,1,1,1,2,0,0]
[0,0,0,2,2,2,2,0,0]
[0,0,0,0,0,0,0,0,0]
On jsFiddle

Categories

Resources