user2100672
user2100672

Reputation:

Counting items in list before consecutive instance

I'd like to count the amount of items in an array before a consecutive amount of zeroes which is inputted by the user.

['1', '0', '1', '0', '0', '0', '0', '0', '0', '0', 'E']

For example, if the user inputs 3, there are three items in the list before a consecutive amount of three zeroes.

At the moment my code checks for the amount of consecutive zeroes and returns based on that - how can I change this to also gain the value of items before the consecutive instance?

LargeBlock = max(sum(1 for _ in g) for k, g in groupby(line) if k == '0')

Upvotes: 2

Views: 147

Answers (3)

Raymond Hettinger
Raymond Hettinger

Reputation: 226396

This is much easier if you first convert the list to string:

  row = ['1', '0', '1', '0', '0', '0', '0', '0', '0', '0', 'E']
  seats = ''.join(row[:-1])

Once in string form, it is easy to search for a block of seats:

  block = '0' * n
  location = s.find(block)

Here is all the code in a single function:

def search(available, required):
    'Return the first available block of seats on a given row'
    row = available[-1]
    seats = ''.join(available[:-1])
    block = '0' * required
    i = seats.find(block)
    if i == -1:
        raise ValueError('no block large enough')
    return '%s%d-%s%d' % (row, i+1, row, i+required)


if __name__ == '__main__':
    print search(['1', '0', '1', '0', '0', '0', '0', '0', '0', '0', 'E'], required=3)

If you want to stick with your original itertools.groupby approach, then you need to track both the position and values as you loop. This is a job for enumerate():

>>> def is_occupied(t):
        seat, occupied = t
        return occupied

>>> def seat_number(t):
        seat, occupied = t
        return seat

>>> required = 3
>>> row = ['1', '0', '1', '0', '0', '0', '0', '0', '0', '0', 'E']
>>> for occupied, groups in groupby(enumerate(row[:-1]), key=is_occupied):
        if occupied == '0':
            seats = list(map(seat_number, groups))
            if len(seats) >= required:
                print(seats[:required])

Eventhough groupby() can be made to work for you, it is tricky to use with a stream of tuples (such as that generated by enumerate()). If you're aiming for clarity, it is something better to skip the functional programming composition tricks and just look normally.

Here is a straight-forward, non-functional approach:

>>> cumulative_open = 0
>>> row_letter = row[-1]

>>> row = ['1', '0', '1', '0', '0', '0', '0', '0', '0', '0', 'E']
>>> required = 3

>>> row_letter = row[-1]
>>> cumulative_open = 0
>>> for i, occupied in enumerate(row[:-1], 1):
        if occupied == "1":
            cumulative_open = 0
            continue
        cumulative_open += 1
        if cumulative_open >= required:
            print('%s%d-%s%d' % (row_letter, i-required+1, row_letter, i))
            break
else:
    print("Not enough open seats")


E4-E6

Upvotes: 5

Inbar Rose
Inbar Rose

Reputation: 43457

Just for fun.... A very basic Theatre/Row manager.

class Row():

    class SeatOccupiedException(Exception):
        pass

    def __init__(self, seats):
        """
        Create a theatre row with seats.

        seats ::= number of seats in the row
        """
        self.seats = [False]*seats

    def get_seats(self, group_size=1, empty=True):
        """
        Get seats from the row in chunks according to group size.
        Can get empty or non-empty seats.

        group_size ::= amount of seats needed.
        empty      ::= should the seats be empty or not?
        """
        ret = []
        current_seats = []

        for idx, seat in enumerate(self.seats, 1):
            if seat != empty:
                current_seats.append(idx)
            if len(current_seats) >= group_size:
                ret.append(current_seats[-group_size:])

        return ret

    def occupy_seats(self, seats):
        """
        Occupy some seats
        """
        for seat in seats:
            if self.seats[seat]:
                raise SeatOccupiedException()
            self.seats[seat] = True

    def vacate_seats(self, seats):
        """
        Vacate some seats
        """        
        for seat in seats:            
            self.seats[seat] = False

class Theatre():            

    class RowAlreadyExistsException(Exception):
        pass

    def __init__(self, rows=None, seats_per_row=10):
        """
        Create a theatre with rows, each row has seats.

        rows          ::= a list of the names for each row
        seats_per_row ::= number of seats in the row

        Examples:
        t = Theatre(['A', 'B', 'C']) 
        => produces a theatre with 3 rows (A,B,C) with 10 seats
        t = Theatre('ABCDEFG', 3) 
        => produces a theatre with 7 rows (A,B,C,D,E,F,G) with 3 seats
        """        
        self.rows = {}
        if rows:
            for row in rows:
                self.add_row(row, seats_per_row)

    def add_row(self, row_id, seats):
        """
        Add a row to the theatre

        row_id ::= the name of the row
        seats  ::= number of seats in the row
        """
        if row_id in self.rows:
            raise RowAlreadyExistsException()
        self.rows[row_id] = Row(seats)

    def get_available_seats(self, group_size, row_id=None):
        """
        Get all seats available for a group of a certain size
        Can specify a specific row or all of them

        group_size ::= how many available seats are needed
        row_id     ::= specify a certain row if desired
        """
        ret = {}

        def get_row(k):
            ret[k] = self.rows[k].get_seats(group_size)

        rows = [row_id] if row_id else self.rows
        for row_id in self.rows:
            get_row(row_id)

        return ret

    def occupy_seats(self, row_id, seats):
        """
        Occupy some seats
        """           
        self.rows[row_id].occupy_seats(seats)

    def vacate_seats(self, row_id, seats):
        """
        Vacate some seats
        """   
        self.rows[row_id].vacate_seats(seats)  

Upvotes: 0

thefourtheye
thefourtheye

Reputation: 239513

seats, wanted = ['1', '0', '1', '0', '0', '0', '0', '0', '0', '0', 'E'], 3
from itertools import groupby
for occ, grp in groupby(enumerate(seats[:-1], 1), key = lambda x: x[1]):
    if occ == '0':
        available = list(grp)
        if len(available) >= wanted:
            print([seats[-1] + str(item[0]) for item in available[:wanted]])

# ['E4', 'E5', 'E6']

Upvotes: 1

Related Questions