Reputation: 33
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
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
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
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
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
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
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