JSFiddle - React, Tailwind, and code Playground

by also

JavaScript

function *makeBorderIter(matrix) {
  const width = matrix[0].length;
  const height = matrix.length;
  
  for (const y of [0, height - 1]) {
  	for (let x = 0; x < width; x++) {
    	yield [x, y];
    }
  }
  
  for (const x of [0, width - 1]) {
  	for (let y = 0; y < height; y++) {
    	yield [x, y];
    }
  }
}

function get(matrix, [x, y]) {
	return matrix[y] ? matrix[y][x] : undefined;
}

function *getAdjacentBlack(matrix, [x, y]) {
  const result = [];
  for (const [xx, yy] of [[-1, 0], [1, 0], [0, -1], [0, 1]]) {
  	const pos = [x + xx, y + yy];
    if (get(matrix, pos) === 1) {
    	yield pos;
    }
  }
}

const matrix = [
  [0, 0, 1, 0],
  [1, 0, 1, 1],
  [0, 1, 0, 0],
  [0, 0, 1, 0],
  [0, 0, 0, 0]
];

const borderIslands = {};

function isBorderIsland(pos) {
  return get(borderIslands, pos);
}

function markAsBorderIsland([x, y]) {
  if (borderIslands[y] != null) {
    borderIslands[y][x] = true
  } else {
    borderIslands[y] = {
      [x]: true
    };
  }
}

for (const pos of makeBorderIter(matrix)) {
	// skip if white
  if (get(matrix, pos) === 0) {
    continue;
  }
  // mark black border as border island
	markAsBorderIsland(pos);
  const adjacent = [...getAdjacentBlack(matrix, pos)];
  // bfs of adjacent black coords
  while (adjacent.length) {
    const pos = adjacent.shift();
    // skip if we've already processed it
    if (isBorderIsland(pos)) {
      continue;
    }
    adjacent.push(...getAdjacentBlack(matrix, pos));
    markAsBorderIsland(pos);
  }
}

// find all black coords that aren't connected to a border island
for (const y in matrix) {
  for (const x in matrix[y]) {
  	if (matrix[y][x] === 1 && !isBorderIsland([x, y])) {
    	matrix[y][x] = null;
    }
  }
}

console.log(matrix);