Checking sequential numbers with a specific condition - Python
I am trying to write a function that will check if a list has consecutive numbers, but with a very strange catch. The catch is that "a" can be used as a substitute for any integer, but at least 2 items in the list must be numbers. Also elements> = 1 (if not "a") and are integers. It can be assumed that the input will be of this shape, so there is no need to validate it. I'm new to coding, so I'd rather use loops over one liner as I'm not very familiar with using one liner yet.
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 was trying 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
It doesn't work because it doesn't account for the "a" values. I'm not sure how to do this. Thanks in advance for any help.
source to share
Try the following. It fits all of your test cases and I was holding back the one-liners:
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 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 see how some one-liners work, you can reduce the function a bit like this:
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
source to share
You have a working solution here. I know you said you didn't want a list comprehension (I edit it with loops for
so you can understand):
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 explanation of the list can be translated into the following:
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)
source to share
This is not a good solution. But I tried the list back 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])
source to share
It works 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
source to share
All of your test tables assume the sequence starts or ends with an actual number (or both). Assuming it does, we can find a fairly simple 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
source to share
All other answers probably work, but I tried to keep everything as simple and simple as possible, I hope this helps :) The code should be clear.
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 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
source to share