Kiwi
Kiwi

Reputation: 33

Checking for consecutive numbers with a specific condition - Python

I'm trying to write a function that would test whether a list has consecutive numbers but with a very odd catch. The catch is that "a" can be used as a replacement for any integer, but at least 2 of the elements of the list must be numbers. Also elements >= 1 (if not "a") and are integers. It can be assumed that the inputs would be of this form, so no need to check for it. I'm new to coding so I'd actually prefer for loops over one liners as I'm not yet very familiar with the use of one liners.

For example:

>>>check_consecutive([1,2,"a","a",5,6,7,"a"])
True
>>>check_consecutive(["a","a","a","a",9,10])
True
>>>check_consecutive(["a","a","a","a",5])
False #Need at least 2 elements to be numbers
>>>check_consecutive([3,4,5,"a",6,7])
False
>>>check_consecutive(["a","a","a","a","a","a",2,3])
False

What I tried to do:

def check_consecutive(lst):
 count = 0
 for i in range(len(lst)-1):
     if lst[i] == "a" or lst[i+1] == "a":
         count +=1
     elif lst[i+1] - lst[i] == 1:
         count +=1
 if count == len(lst)-1:
     return True
 return False

This isn't working because it's fails to take into account the values of "a". I'm not sure how to go about doing this. Thanks in advance for any help.

Upvotes: 3

Views: 2877

Answers (6)

Łukasz Rogalski
Łukasz Rogalski

Reputation: 23203

All of your testcases assume that sequence either start or finish with actual number (or both). Assuming this is true, we may come up with quite easy explicit solution in O(n) time and O(1) space.

def check_consecutive(seq):
    PLACEHOLDER = 'a'
    # O(n) check for preconditions
    if sum(obj != PLACEHOLDER for obj in seq) < 2:
        return False
    if isinstance(seq[0], int):
        # first element known, incrementing
        incrementing = True
        delta = 1
    else:
        # last element known, decrementing
        incrementing = False
        delta = -1
    # iterate from first or last element
    iterator = iter(seq) if incrementing else reversed(seq)
    # consume first value
    previous_item = next(iterator)
    # check if our assumption is correct
    assert previous_item != PLACEHOLDER
    # O(n) check for valid sequence
    for item in iterator:
        # figure out expected placeholder value
        if item == PLACEHOLDER:
            item = previous_item + delta
        # check if next value matches requirements of valid solution
        if item <= 0 or item != (previous_item + delta):
            return False
        previous_item = item
    return True

assert check_consecutive([1,2,"a","a",5,6,7,"a"]) is True
assert check_consecutive(["a","a","a","a",9,10]) is True
assert check_consecutive(["a","a","a","a",5]) is False
assert check_consecutive([3,4,5,"a",6,7]) is False
assert check_consecutive(["a","a","a","a","a","a",2,3]) is False

Upvotes: 1

PdevG
PdevG

Reputation: 3677

All the other answer probably work, but I tried keeping things as simple and as straightforward as I could, hope it helps :) Code should be self-explanatory.

def check_consecutive(input_list):
    # First find the first element that is not equal to a to figure out the start of the sequence
    start_seq = None
    for i, el in enumerate(input_list):
        if el != 'a':
            # Now we know the number the sequence should start with.
            # For example the first 2 elements are a, and the first "non-a" element is 5
            # Then the loop breaks when el = 5, and i = 2, meaning the start should be 3
            start_seq = el - i
            break

    # If the whole list exists of a's, then start will still be None and the function should return false
    if start_seq is None:
        return False

    if start_seq < 1:
        return False

    # Now we can loop over the list, keep replacing a's by what they should be
    # Create some values for bookkeeping
    num_n = 0
    old_value = start_seq - 1
    for el in input_list:
        if el != 'a':
            num_n += 1
            if el != (old_value + 1):
                return False

        # increment old value
        old_value += 1

    if num_n < 2:
        return False

    return True

Upvotes: 1

Yasin Yousif
Yasin Yousif

Reputation: 967

It is working for your examples (py 2.7 ):

def check_consecutive(lst):
    # what could be the list
    cond = [0,1]
    n_sum = False
    for x in lst:
        if not(cond[1]) and x!= 'a':
            n_sum = True;
            if (cond[0] - x) != (lst.index(cond[0]) - lst.index(x)): 
                return False
            elif x <= lst.index(x):
                return False
        if x!='a' and cond[1]:
            cond[0] = x;
            cond[1] = 0;
    print cond
    return n_sum

Upvotes: 1

kuro
kuro

Reputation: 3226

It is not so good solution. But I tried the list backwards like this -

def check_consecutive(lst):
  last = 0
  number_count = len(lst) - lst.count('a')
  # Check count of numbers and return if less than 2
  if number_count < 2:
      return False

  for i in reversed(range(len(lst))):
      # last 0 means no numbers encountered yet
      if not last:
          last = lst[i] if lst[i] != "a" else 0
          continue
      else:
          # If current element is number check consecutive property
          if lst[i] != "a" and last - lst[i] != 1:
              return False

          # Recalculate last
          last -= 1

          # If last falls below permissible range, return 
          if last < 1:
              return False

  # If all condition satisfied, it is a hit. Whew!!!
  return True

print check_consecutive([1,2,"a","a",5,6,7,"a"])
print check_consecutive(["a","a","a","a",9,10])
print check_consecutive(["a","a","a","a",5])
print check_consecutive([3,4,5,"a",6,7])
print check_consecutive(["a","a","a","a","a","a",2,3])

Upvotes: 1

Carles Mitjans
Carles Mitjans

Reputation: 4866

Here you have a working solution. I know you said you don't want list comprehensions (I am editing it with for loops so you can understant):

def check_consecutive(a):
    b = [(next(y for y in a if y!='a')-a.count('a') if a[0] == 'a' else a[0])+i if x=='a' else x for i, x in enumerate(a)]
    return len(a) - a.count('a') >= 2 and b[0] >= 0 and range(b[0],b[-1]+1) == b

The list comprehension can be translated to this:

for i,x in enumerate(a):
     if x == 'a':
         if a[0] == 'a':
             #              |---> Gets first integer from list
             new_lst.append(next(x for x in a if x != 'a') - a.count('a'))
         else:
             new_lst.append(a[0]+i)
     else:
         new_lst.append(x)

Upvotes: 1

asongtoruin
asongtoruin

Reputation: 10359

Try the following. It matches for all of your test cases, and I've kept one-liners to a minimum:

def check_consecutive(lst):
    # Check for the number of non-"a" entries in the list:
    if len([x for x in lst if x != "a"]) < 2:
        return False

    # Get the first non-a value (ASSUMPTION: this is a number)
    first_number = next((e for e in lst if e != "a"), None)

    # Find the index of the first number
    first_index = lst.index(first_number)

    # Find the difference between the first number and its index
    diff = first_number - first_index

    # Based on the final example - false if negative values would be used:
    if diff < 0:
        return False

    # Create a new list - replace a's with their index plus the difference we found
    # If the list is consecutive, this difference will be consistent for all values
    all_numbers = []
    for i, x in enumerate(lst):
        if x == "a":
            all_numbers.append(i + diff)
        else:
            all_numbers.append(x)

    # Check if the values are now consecutive or not!
    if all(all_numbers[i+1] == all_numbers[i] + 1 for i in range(len(all_numbers) - 1)):
        return True
    else:
        return False

print check_consecutive([1,2,"a","a",5,6,7,"a"])
#True
print check_consecutive(["a","a","a","a",9,10])
#True
print check_consecutive(["a","a","a","a",5])
#False #Need at least 2 elements to be numbers
print check_consecutive([3,4,5,"a",6,7])
#False
print check_consecutive(["a","a","a","a","a","a",2,3])
#False

If you want to look at how some one-liners work, you can reduce the function down slightly as follows:

def check_consecutive(lst):
    # Check for the number of non-"a" entries in the list:
    if len([x for x in lst if x != "a"]) < 2:
        return False

    # Get the first non-a value (ASSUMPTION: this is a number)
    first_number = next((e for e in lst if e != "a"), None)

    # Find the index of the first number
    first_index = lst.index(first_number)

    # Find the difference between the first number and its index
    diff = first_number - first_index

    # Based on the final example - false if negative values would be used:
    if diff < 0:
        return False

    if all(x == "a" or x == i + diff for i, x in enumerate(lst)):
        return True
    else:
        return False

Upvotes: 3

Related Questions