Skip to content

Commit d920e7f

Browse files
vil02appgurueu
andauthored
refactor: reduce code duplication in FloodFill (TheAlgorithms#1645)
* tests: add tests checking if floodFill funtions throw when location is outside * refactor: reduce code duplication by adding `checkLocation` to `FloodFill` * refactor: add and use `isInside` Co-authored-by: appgurueu <34514239+appgurueu@users.noreply.github.com> * Deduplicate further --------- Co-authored-by: appgurueu <34514239+appgurueu@users.noreply.github.com>
1 parent d02e402 commit d920e7f

File tree

2 files changed

+41
-31
lines changed

2 files changed

+41
-31
lines changed

Recursive/FloodFill.js

+28-31
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* @see https://www.techiedelight.com/flood-fill-algorithm/
1010
*/
1111

12-
const neighbors = [
12+
const neighborOffsets = [
1313
[-1, -1],
1414
[-1, 0],
1515
[-1, 1],
@@ -20,6 +20,27 @@ const neighbors = [
2020
[1, 1]
2121
]
2222

23+
function isInside(rgbData, location) {
24+
const x = location[0]
25+
const y = location[1]
26+
return x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length
27+
}
28+
29+
function checkLocation(rgbData, location) {
30+
if (!isInside(rgbData, location)) {
31+
throw new Error('location should point to a pixel within the rgbData')
32+
}
33+
}
34+
35+
function* neighbors(rgbData, location) {
36+
for (const offset of neighborOffsets) {
37+
const neighborLocation = [location[0] + offset[0], location[1] + offset[1]]
38+
if (isInside(rgbData, neighborLocation)) {
39+
yield neighborLocation
40+
}
41+
}
42+
}
43+
2344
/**
2445
* Implements the flood fill algorithm through a breadth-first approach using a queue.
2546
*
@@ -34,14 +55,7 @@ export function breadthFirstSearch(
3455
targetColor,
3556
replacementColor
3657
) {
37-
if (
38-
location[0] < 0 ||
39-
location[0] >= rgbData.length ||
40-
location[1] < 0 ||
41-
location[1] >= rgbData[0].length
42-
) {
43-
throw new Error('location should point to a pixel within the rgbData')
44-
}
58+
checkLocation(rgbData, location)
4559

4660
const queue = []
4761
queue.push(location)
@@ -65,14 +79,7 @@ export function depthFirstSearch(
6579
targetColor,
6680
replacementColor
6781
) {
68-
if (
69-
location[0] < 0 ||
70-
location[0] >= rgbData.length ||
71-
location[1] < 0 ||
72-
location[1] >= rgbData[0].length
73-
) {
74-
throw new Error('location should point to a pixel within the rgbData')
75-
}
82+
checkLocation(rgbData, location)
7683

7784
depthFirstFill(rgbData, location, targetColor, replacementColor)
7885
}
@@ -98,13 +105,8 @@ function breadthFirstFill(
98105

99106
if (rgbData[currentLocation[0]][currentLocation[1]] === targetColor) {
100107
rgbData[currentLocation[0]][currentLocation[1]] = replacementColor
101-
102-
for (let i = 0; i < neighbors.length; i++) {
103-
const x = currentLocation[0] + neighbors[i][0]
104-
const y = currentLocation[1] + neighbors[i][1]
105-
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
106-
queue.push([x, y])
107-
}
108+
for (const neighborLocation of neighbors(rgbData, currentLocation)) {
109+
queue.push(neighborLocation)
108110
}
109111
}
110112
}
@@ -120,13 +122,8 @@ function breadthFirstFill(
120122
function depthFirstFill(rgbData, location, targetColor, replacementColor) {
121123
if (rgbData[location[0]][location[1]] === targetColor) {
122124
rgbData[location[0]][location[1]] = replacementColor
123-
124-
for (let i = 0; i < neighbors.length; i++) {
125-
const x = location[0] + neighbors[i][0]
126-
const y = location[1] + neighbors[i][1]
127-
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
128-
depthFirstFill(rgbData, [x, y], targetColor, replacementColor)
129-
}
125+
for (const neighborLocation of neighbors(rgbData, location)) {
126+
depthFirstFill(rgbData, neighborLocation, targetColor, replacementColor)
130127
}
131128
}
132129
}

Recursive/test/FloodFill.test.js

+13
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ describe('FloodFill', () => {
2121
})
2222
})
2323

24+
describe.each([breadthFirstSearch, depthFirstSearch])('%o', (floodFillFun) => {
25+
it.each([
26+
[1, -1],
27+
[-1, 1],
28+
[0, 7],
29+
[7, 0]
30+
])('throws for start position [%i, %i]', (location) => {
31+
expect(() =>
32+
floodFillFun(generateTestRgbData(), location, green, orange)
33+
).toThrowError()
34+
})
35+
})
36+
2437
/**
2538
* Utility-function to test the function "breadthFirstSearch".
2639
*

0 commit comments

Comments
 (0)