Clip 2d List Untill Contents Form a Rectangle (Python)

Viewed 61

In a level editor I have to make, I need to take a 2d list with lots of empty spaces (0's) and clip and add to it untill there are no 0's on the outside, inside is ok. The first thing that I need to do is clip off all the extra 0's on each side ex:

map = [
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0],
  [0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0],
  [0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0],
  [0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0],
  [0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0],
  [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]

def clipMap(map):
  """Need the code for"""

map = clipMap(map)

#map should now be:
  [1, 1, 1, 1, 1, 1, 1, 1],
  [1, 0, 0, 1, 0, 0, 1, 1],
  [1, 0, 0, 1, 0, 1, 0, 1],
  [1, 1, 1, 1, 1, 0, 0, 1],
  [1, 0, 1, 0, 0, 1, 0, 1],
  [1, 0, 1, 0, 0, 1, 0, 1],
  [1, 1, 1, 1, 1, 1, 1, 1]

Note that I am just using 1 and 0, when in the editor there are lots of different blocks that would be 1 in this case.

EDIT: I have tried this, but it was really buggy:

def clipMap(map):
  minX = 0
  minY = 0
  maxX = len(map[0])
  maxY = len(map)


  #Find min and max
  for y in map:
    for x in map[y]:
      if map[y][x] != 0:
        if x < minX:
          minX = x
        else if x > maxX:
          maxX = x

        if y < minY:
          minY = y
        else if y > maxY:
          maxY = y

  #remove them
  X = slice(minX, maxX)
  Y = slice(minY, maxY)
  map = map[Y]
  for y in map:
    map[y] = map[y][X]

  return map




1 Answers

A naive version might be:

def truthy_idx(it):
    return next(i for i, x in enumerate(it) if x)

res = [x[truthy_idx(x):len(x)-truthy_idx(x[::-1])] for x in map if any(x)]

This selects all rows that have at least one nonzero value in them and trims each selected row by its left and right nonzero element. I'm using a function truthy_idx to grab the first nonzero (truthy) index in an iterable, along with a slice to reverse the list and get the last index with len(row) - truthy_idx(row[::-1]).

This isn't suitable for grids with jagged edges, but it works on the rectangular case which is the situation here.

As an aside, calling a variable map overwrites a builtin function.


If you can use numpy, another approach is:

np.apply_along_axis(np.trim_zeros, 1, a[a.any(1)])
Related