MTG
MTG

Reputation: 163

Python - Move Elements in a 2-Dimensional Array?

Given this 2D Array -

[[0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3], [0, 0, 0]]

How can I code something in Python that shifts once to the end each time it's called? However, it has to stop if it reaches a positive value.

So I get something like this

[[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]]

This is what I have. It works for condition 1 but it does not work for condition 2. What can I tweak so condition 2 also works?

# Condition 1 - Works
data = [[0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0], [0, 0, 0]]
expected = [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0]] # My Results. 

# Condition 2 - Does Not Work
data = [[0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3], [0, 0, 0]]
expected = [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]] # This is what I want to get. (But it doesn't work)

rows = len(data)

if data[-1][-1] == 0:
    count = rows-1
    while count > 0:
        data[count][-1] = data[count-1][-1]
        count -= 1
    data[0][-1] = 0

print(data)
print(expected)

This is what I am getting currently. I want to get the expected for Condition 2 listed in the code snipped above.:

Condition 2 Result: [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3]]

Thanks

Update @Furas: Something like this to find the positive value location?

   possitive_value = []
    for i in range(len(data)):
        if data[i][-1] > 0:
            possitive_value.append(i, -1)

Upvotes: 1

Views: 4241

Answers (2)

Antoine Zambelli
Antoine Zambelli

Reputation: 764

Here's a variation, a function that can be called and will shift data by one every time. I wasn't entirely clear from your question if this was the behavior you wanted.

import numpy as np

def shift_data(data):
    ele_idx = [i  for i,x in enumerate(data) if x != [0,0,0]]
    zero_idx = [i for i,x in enumerate(data) if x == [0,0,0] and i != 0]

    if max(np.diff(ele_idx)) == 1 or max(np.diff([i for i,x in enumerate(data) if x == [0,0,0]])) == 1:#things are consecutive
        data.insert(0,data.pop(-1))
    else:
        tt = [l for l in data[ele_idx[0]:zero_idx[0]+1]]
        tt.insert(0,tt.pop(-1))
        data = data[:ele_idx[0]] + tt + data[zero_idx[0]+1:]

    return data

data = [[0, 0, -1], [0, 0, 0], [0, 0, -2], [0, 0, 0], [0, 0, 3]]
print(data)
for _ in range(0,6):
    data = shift_data(data)
    print(data)

This code outputs:

[[0, 0, -1], [0, 0, 0], [0, 0, -2], [0, 0, 0], [0, 0, 3]]
[[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3]]
[[0, 0, 0], [0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3]]
[[0, 0, 3], [0, 0, 0], [0, 0, 0], [0, 0, -1], [0, 0, -2]]
[[0, 0, -2], [0, 0, 3], [0, 0, 0], [0, 0, 0], [0, 0, -1]]
[[0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0], [0, 0, 0]]
[[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]]

Upvotes: 0

furas
furas

Reputation: 142661

I think you have to find first positive value and use its position as rows

EDIT: I changed name count to last to make it more readable

# --- function ---

def move(data):

    rows = len(data)

    # - find positive -

    for x in range(rows):
        if data[x][-1] > 0:
            rows = x # use its position as `rows`
            break    # don't seach other positiove values 

    # - star moving -

    # set "last" checked row
    last = rows-1

    # check "last" row
    if data[last][-1] == 0:

        # move previous values
        while last > 0:
            data[last][-1] = data[last-1][-1]
            last -= 1

        # put 0 in first place    
        data[0][-1] = 0

# --- tests ---

examples = [
  {
    # Condition 1 - Works
    'data': [[0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0], [0, 0, 0]],
    'expected': [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0]], # My Results. 
  },
  {
    # Condition 2 - Works
    'data': [[0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3], [0, 0, 0]],
    'expected': [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]], # This is what I want to get. (But it doesn't work)
  }
]

for example in examples:
    data = example['data']
    expected = example['expected']

    print('  before:', data)
    move(data)
    print('   after:', data)
    print('expected:', expected)
    print(' correct:', data == expected)
    print('---')

Result:

  before: [[0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0], [0, 0, 0]]
   after: [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0]]
expected: [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0]]
 correct: True
---
  before: [[0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3], [0, 0, 0]]
   after: [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]]
expected: [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]]
 correct: True
---

EDIT:

BTW: instead of while you can use for with reversed range() if it makes it more readable

def move(data):

    rows = len(data)

    # - find positive -

    for x in range(rows):
        if data[x][-1] > 0:
            rows = x # use its position as `rows`
            break    # don't seach other positiove values 

    # - star moving -

    # set "last" checked row
    last = rows-1

    # check "last" row
    if data[last][-1] == 0:

        # move previous values
        for pos in range(last, 0, -1): # range with reversed order
            data[pos][-1] = data[pos-1][-1]

        # put 0 in first place    
        data[0][-1] = 0

BTW: both versions move items in original data ("in-place") so they don't need return data.

Upvotes: 2

Related Questions