Python: list manipulation using del or list.remove - python

I am trying to write a function. This function takes a list of numbers as input, finds the largest sequence of consecutive numbers in this list and returns a list containing only this largest sequence of numbers of the original list.
Example:
In [2]: largestSeq([1,2,3,4,1,2,3])
Out[2]: [1, 2, 3, 4]
It works as long as the input list has 0 or more than 1 elements in it.
I included print statements in my code to see where the error is.
Here is the code and the result of calling largestSeq([1])and largestSeq([1,2]):
Code:
def findSeq(seq): #this finds a sequence of consecutive numbers
i = 0 #in a list and returns it
if len(seq) <= 1: #it stops when the next number in the list
return seq #is smaller than the former
s =[seq[0]]
while seq[i] < seq[i+1]:
i += 1
s.append(seq[i])
if i == len(seq)-1:
break
return s
def largestSeq(seq,a=[]):
b = findSeq(seq) #find the first consecutive sequence
if len(seq) == 0:
return a
print 'Length of b is ' + str(len(b))
if len(b) > len(a): #check if found sequence is bigger than
print 'seq is now ' + str(seq)#the last found sequence
print 'b is now ' + str(b)
i = len(b)
print 'now deleting elements of seq'
for d in range (i):
seq.remove(seq[0]) #remove found sequence from the original
#del seq[0:i] #list
print 'seq is now ' + str(seq)
print 'b is now ' + str(b)
return largestSeq(seq,b) #start over
else:
del seq[0:len(b)]
return largestSeq(seq,a)
And now the calls:
In [14]: largestSeq([1])
Length of b is 1
seq is now [1]
b is now [1]
now deleting elements of seq
seq is now []
b is now []
Out[14]: []
largestSeq([1,2])
Length of b is 2
seq is now [1, 2]
b is now [1, 2]
now deleting elements of seq
seq is now []
b is now [1, 2]
Out[15]: [1, 2]
Please note, that in the first call, the element in b is also deleted after deleting element of seq , althoug I didn't change it!
In the second call with [1,2] b is behaving like I want it, while seq is deleted.
I tried manipulating the list with list.remove and with del (which is commented out and yields the same error).
What is going on in there? I don't understand it.
I want b remaining unchanged in the first call, like it does in the second call.
This is a very specific question. I would be grateful for any suggestions!

In the first case you are returning the same list, you have to return a copy of the list.
Try:
def findSeq(seq): #this finds a sequence of consecutive numbers
i = 0 #in a list and returns it
if len(seq) <= 1: #it stops when the next number in the list
return list(seq) #is smaller than the former
s =[seq[0]]
while seq[i] < seq[i+1]:
i += 1
s.append(seq[i])
if i == len(seq)-1:
break
return s

Related

Index value not in list

I need to write code that returns 2 index numbers of an array. The function takes 2 arguments, one is an array and the other is an integer.
I need to check if two of the values inside the array adds up to the integer and using the numbers inside the array only once.
Here is my code:
def func(a,b):
for i in a:
cnt = 0
while cnt < len(a):
if i + a[cnt] == b and i != a[cnt]:
return list([i,a[cnt]])
else:
cnt += 1
print(func([3,7,2,10,20],27))
My output for func([3, 7, 2, 10, 20], 27) is [7, 20].
This code shows that the loop can find the numbers which add up to the integer.
But when I do this:
def func(a,b):
for i in a:
cnt = 0
while cnt < len(a):
if i + a[cnt] == b and i != a[cnt]:
return a.index(i,a[cnt])
else:
cnt += 1
print(func([3,7,2,10,20],27))
I get the Value error: 7 not in list, which clearly is.
I've had this issue working with other exercises as well.
Am I doing something wrong or the index function isn't suppose to be used like that.
What would be an efficient way to return the index numbers without having to write another loop for it?
The second parameter to index that you're passing is actually the starting index on the list in which the search for the element will start (as you can see here). If you remove it, you'll see that it returns the first value you want (but not the second). It is relevant to note that the index method will only ever return the first occurrence of the value.
def func(a,b):
for i in a:
cnt = 0
while cnt < len(a):
if i + a[cnt] == b and i != a[cnt]:
return a.index(i)
else:
cnt += 1
print(func([3,7,2,10,20],27))
>>> 1
This happens because the value you're passing as the starting index (a[cnt]) is greater than the actual index of the number (1).
By removing it, you search through all the list, and find the correct value (remembering that Python uses zero-indexed iterators).
But since you want to return a list with both values, you need to explicitly state you want the index for each, such as:
def func(a,b):
for i in a:
cnt = 0
while cnt < len(a):
if i + a[cnt] == b and i != a[cnt]:
return [a.index(i), a.index(a[cnt])]
else:
cnt += 1
print(func([3,7,2,10,20],27))
>>> [1, 4]
You could achieve the same results using two for loops, however, in a cleaner way (also gave meaningful names to variables):
def find_indices_for_sum(array, sum_value):
for i in array:
for j in array:
if i + j == sum_value and i != j:
return [array.index(i), array.index(j)]
print(find_indices_for_sum([3,7,2,10,20],27))
>>> [1, 4]
If you want to be able to deal with equal numbers, you can change the comparison strategy altogether, using indices instead of values, since the former are unique in a list, but the latter are not. enumerate is a good option here, since it allows to iterate both through index and values at the same time in a clean way.
def find_indices_for_sum(array, sum_value):
for i, value_i in enumerate(array):
for j, value_j in enumerate(array):
if i != j and value_i + value_j == sum_value:
return [i, j]
print(find_indices_for_sum([3,3],6))
>>> [0, 1]

Count consecutive digits in binary string

For a homework problem, we are asked to define a function that will count the number of consecutive digits in a binary string, and return the number.
For example, the function should return n = [4,8,4,3,15] for the binary input S = ‘1111000000001111000111111111111111’.
I have this so far, but I know it's not correct, and I do not know where to go from here. Any help would be appreciated!
def consecutive_length(s):
if s == '':
return 0
if s[0] == 0:
return 0
return 1 + consecutive_length(s[1:])
Note: we cannot use any loops. It is required that we do this with recursion.
Thank you!
Here's a hopefully pythonic way (ignoring the fact that it's not pythonic to solve this kind of problem recursively):
def consecutive_length(s):
def sub(idx, lst, last_char, count):
try:
c = s[idx] # c will be the 'next' char
except IndexError: # no more chars left to process
if count:
lst.append(count)
return lst
if c != last_char:
lst.append(count)
count = 0
return sub(idx+1, lst, c, count+1)
return sub(0, [], s[0] if s else None, 0)
where
the outer function just takes the string as an argument and hides the inner functions additional parameters
idx is the index to the string, we don't allocate a new string at every recursive call (and s[idx] is O(1) iirc)
instead of computing the length of the string, we wait for an exception to happen (EAFP - Easier to ask for forgiveness than permission)
Testing:
>>> print consecutive_length('1111000000001111000111111111111111')
[4, 8, 4, 3, 15]
>>> print consecutive_length('1111000000001111000111111111111110')
[4, 8, 4, 3, 14, 1]
>>> print consecutive_length('1')
[1]
>>> print consecutive_length('0')
[1]
>>> print consecutive_length('')
[]
I am assuming here the '11' is a consecutive sequence of 1's .So '111' has 2 consecutive one's. This solution is, if loops is not a problem. Use index to find '11' and keep doing it until you find no more. The below program shows the number of consecutive 1's .
cnt = 0
pos = -1
while True:
try:
pos = '111001100101111'.index('11', pos+1)
cnt += 1
except ValueError:
print cnt
break
Result:
6
EDIT: uselpa has a much better way of doing it.
Since loops aren't allowed:
def consecutive_length(s, output, prev_char, count):
# if end of string, append last count and return output
if s == '':
output.append(count)
return output
# if curr_char is same as prev_char, add 1 to count and parse next char
if s[0] == prev_char:
return consecutive_length(s[1:], output, s[0], count + 1)
# if curr_char is diff from prev_char, append count and reset count to 1
else:
prev_char = s[0]
output.append(count)
return consecutive_length(s[1:], output, s[0], 1)
Call it with consecutive_length(s, [], s[0], 0).

How to see if the list contains consecutive numbers

I want to test if a list contains consecutive integers and no repetition of numbers.
For example, if I have
l = [1, 3, 5, 2, 4, 6]
It should return True.
How should I check if the list contains up to n consecutive numbers without modifying the original list?
I thought about copying the list and removing each number that appears in the original list and if the list is empty then it will return True.
Is there a better way to do this?
For the whole list, it should just be as simple as
sorted(l) == list(range(min(l), max(l)+1))
This preserves the original list, but making a copy (and then sorting) may be expensive if your list is particularly long.
Note that in Python 2 you could simply use the below because range returned a list object. In 3.x and higher the function has been changed to return a range object, so an explicit conversion to list is needed before comparing to sorted(l)
sorted(l) == range(min(l), max(l)+1))
To check if n entries are consecutive and non-repeating, it gets a little more complicated:
def check(n, l):
subs = [l[i:i+n] for i in range(len(l)) if len(l[i:i+n]) == n]
return any([(sorted(sub) in range(min(l), max(l)+1)) for sub in subs])
The first code removes duplicates but keeps order:
from itertools import groupby, count
l = [1,2,4,5,2,1,5,6,5,3,5,5]
def remove_duplicates(values):
output = []
seen = set()
for value in values:
if value not in seen:
output.append(value)
seen.add(value)
return output
l = remove_duplicates(l) # output = [1, 2, 4, 5, 6, 3]
The next set is to identify which ones are in order, taken from here:
def as_range(iterable):
l = list(iterable)
if len(l) > 1:
return '{0}-{1}'.format(l[0], l[-1])
else:
return '{0}'.format(l[0])
l = ','.join(as_range(g) for _, g in groupby(l, key=lambda n, c=count(): n-next(c)))
l outputs as: 1-2,4-6,3
You can customize the functions depending on your output.
We can use known mathematics formula for checking consecutiveness,
Assuming min number always start from 1
sum of consecutive n numbers 1...n = n * (n+1) /2
def check_is_consecutive(l):
maximum = max(l)
if sum(l) == maximum * (maximum+1) /2 :
return True
return False
Once you verify that the list has no duplicates, just compute the sum of the integers between min(l) and max(l):
def check(l):
total = 0
minimum = float('+inf')
maximum = float('-inf')
seen = set()
for n in l:
if n in seen:
return False
seen.add(n)
if n < minimum:
minimum = n
if n > maximum:
maximum = n
total += n
if 2 * total != maximum * (maximum + 1) - minimum * (minimum - 1):
return False
return True
import numpy as np
import pandas as pd
(sum(np.diff(sorted(l)) == 1) >= n) & (all(pd.Series(l).value_counts() == 1))
We test both conditions, first by finding the iterative difference of the sorted list np.diff(sorted(l)) we can test if there are n consecutive integers. Lastly, we test if the value_counts() are all 1, indicating no repeats.
I split your query into two parts part A "list contains up to n consecutive numbers" this is the first line if len(l) != len(set(l)):
And part b, splits the list into possible shorter lists and checks if they are consecutive.
def example (l, n):
if len(l) != len(set(l)): # part a
return False
for i in range(0, len(l)-n+1): # part b
if l[i:i+3] == sorted(l[i:i+3]):
return True
return False
l = [1, 3, 5, 2, 4, 6]
print example(l, 3)
def solution(A):
counter = [0]*len(A)
limit = len(A)
for element in A:
if not 1 <= element <= limit:
return False
else:
if counter[element-1] != 0:
return False
else:
counter[element-1] = 1
return True
The input to this function is your list.This function returns False if the numbers are repeated.
The below code works even if the list does not start with 1.
def check_is_consecutive(l):
"""
sorts the list and
checks if the elements in the list are consecutive
This function does not handle any exceptions.
returns true if the list contains consecutive numbers, else False
"""
l = list(filter(None,l))
l = sorted(l)
if len(l) > 1:
maximum = l[-1]
minimum = l[0] - 1
if minimum == 0:
if sum(l) == (maximum * (maximum+1) /2):
return True
else:
return False
else:
if sum(l) == (maximum * (maximum+1) /2) - (minimum * (minimum+1) /2) :
return True
else:
return False
else:
return True
1.
l.sort()
2.
for i in range(0,len(l)-1)))
print(all((l[i+1]-l[i]==1)
list must be sorted!
lst = [9,10,11,12,13,14,15,16]
final = True if len( [ True for x in lst[:-1] for y in lst[1:] if x + 1 == y ] ) == len(lst[1:]) else False
i don't know how efficient this is but it should do the trick.
With sorting
In Python 3, I use this simple solution:
def check(lst):
lst = sorted(lst)
if lst:
return lst == list(range(lst[0], lst[-1] + 1))
else:
return True
Note that, after sorting the list, its minimum and maximum come for free as the first (lst[0]) and the last (lst[-1]) elements.
I'm returning True in case the argument is empty, but this decision is arbitrary. Choose whatever fits best your use case.
In this solution, we first sort the argument and then compare it with another list that we know that is consecutive and has no repetitions.
Without sorting
In one of the answers, the OP commented asking if it would be possible to do the same without sorting the list. This is interesting, and this is my solution:
def check(lst):
if lst:
r = range(min(lst), max(lst) + 1) # *r* is our reference
return (
len(lst) == len(r)
and all(map(lst.__contains__, r))
# alternative: all(x in lst for x in r)
# test if every element of the reference *r* is in *lst*
)
else:
return True
In this solution, we build a reference range r that is a consecutive (and thus non-repeating) sequence of ints. With this, our test is simple: first we check that lst has the correct number of elements (not more, which would indicate repetitions, nor less, which indicates gaps) by comparing it with the reference. Then we check that every element in our reference is also in lst (this is what all(map(lst.__contains__, r)) is doing: it iterates over r and tests if all of its elements are in lts).
l = [1, 3, 5, 2, 4, 6]
from itertools import chain
def check_if_consecutive_and_no_duplicates(my_list=None):
return all(
list(
chain.from_iterable(
[
[a + 1 in sorted(my_list) for a in sorted(my_list)[:-1]],
[sorted(my_list)[-2] + 1 in my_list],
[len(my_list) == len(set(my_list))],
]
)
)
)
Add 1 to any number in the list except for the last number(6) and check if the result is in the list. For the last number (6) which is the greatest one, pick the number before it(5) and add 1 and check if the result(6) is in the list.
Here is a really short easy solution without having to use any imports:
range = range(10)
L = [1,3,5,2,4,6]
L = sorted(L, key = lambda L:L)
range[(L[0]):(len(L)+L[0])] == L
>>True
This works for numerical lists of any length and detects duplicates.
Basically, you are creating a range your list could potentially be in, editing that range to match your list's criteria (length, starting value) and making a snapshot comparison. I came up with this for a card game I am coding where I need to detect straights/runs in a hand and it seems to work pretty well.

How is this list index out of range? (Fibonacci numbers exercice)

This code should print the sum of the even numbers in the first ten numbers of the Fibonacci sequence.
#Creates a list with the first ten Fibonacci numbers.
l = [1,2]
for i in range(10):
l.append(l[i]+l[i+1])
for i in l:
#If an element of the Fibonacci list is uneven, replace it with zero.
if l[i]%2 != 0:
l[i] = 0
#Print the sum of the list with all even Fibonacci numbers.
print sum(l)
When I execute this I get:
File "pe2m.py", line 6, in <module>
if l[i]%2 != 0:
IndexError: list index out of range
I don't get how its going out of range, could someone clarify?
Your problem is for i in l: it doesn't give you the indices, it gives you the list elements. As the elements are integers, they could be valid (and the first few will be) but they don't have the values you want -- you'll have to iterate over a range again.
You are looping over the values not the index positions!
Use the following code instead:
#Creates a list with the first ten Fibonacci numbers.
l = [1,2]
for i in range(10):
l.append(l[i]+l[i+1])
for i in range(len(l)):
#If an element of the Fibonacci list is uneven, replace it with zero.
if l[i]%2 != 0:
l[i] = 0
#Print the sum of the list with all even Fibonacci numbers.
print sum(l)
You cannot index a list with the value from the list as it is not guaranteed that the value will be within the list boundary
Seeing your code, I feel you are planning to do something as below
>>> for i,e in enumerate(l):
#If an element of the Fibonacci list is uneven, replace it with zero.
if e%2 != 0:
l[i] = 0
Interestingly you can do the same as below. (Edited after seeing glglgl's comment]
>>> print sum(e for e in l if e%2)
Python's for x in y construct returns the values/elements of a sequence in x, not their index.
As for the Fibonacci numbers: The sequence starts with 1, 1 and not 1, 2. And the sum can be done simpler like this:
a, b = 1, 1
s = 0
for i in range(10):
a, b = b, a+b
if b % 2 = 0:
s += b
print s
If you need to get the sum of the first N even numbers, you would do:
a, b = 1, 1
s = 0
count = 0
while count < 10:
a, b = b, a+b
if b % 2 = 0:
s += b
count += 1
print s
And just for fun the version with generators in a functional style:
from itertools import islice
def fib():
a, b = 1, 1
yield a
yield b
while True:
a, b = b, a+b
yield b
even_sum = reduce(lambda x, y: x+y if y % 2 == 0 else x, islice(fib(), 10), 0)

creating a reverse method for a python list from scratch

I want to create a reverse method for a list. I know there already is such a method built into python but I want to try it from scratch. Here's what I have and it seems to make sense to me but it just returns the list in the same order. My understanding was that lists are mutable and I could just reassign values in the loop.
def reverse(data_list):
length = len(data_list)
s = length
for item in data_list:
s = s - 1
data_list[s] = item
return data_list
def reverse(data_list):
return data_list[::-1]
>> reverse([1,2,3,4,5])
[5, 4, 3, 2, 1]
By the time you are half-way through the list, you have swapped all the items; as you continue through the second half, you are swapping them all back to their original locations again.
Instead try
def reverse(lst):
i = 0 # first item
j = len(lst)-1 # last item
while i<j:
lst[i],lst[j] = lst[j],lst[i]
i += 1
j -= 1
return lst
This can be used in two ways:
a = [1,2,3,4,5]
reverse(a) # in-place
print a # -> [5,4,3,2,1]
b = reverse(a[:]) # return the result of reversing a copy of a
print a # -> [5,4,3,2,1]
print b # -> [1,2,3,4,5]
You are changing the list that you iterate on it (data_list) because of that it's not working , try like this:
def reverse(data_list):
length = len(data_list)
s = length
new_list = [None]*length
for item in data_list:
s = s - 1
new_list[s] = item
return new_list
an easy way in python (without using the reverse function) is using the [] access operator with negative values such as (print and create a new list in reverse order):
x = [1, 2 ,3, 4, 5]
newx = []
for i in range(1, len(x)+1):
newx.append(x[-i])
print x[-i]
the function would be:
def reverse(list):
newlist = []
for i in range(1, len(list)+1):
newlist.append(list[-1])
return newlist
I do not get the same list when I try to run your code. But I also do not get a reversed list because the list is moving forward through the list state which is changing from end back. I think the way you are looking to do it is:
def reverse(data_set):
length = len(data_set)
for i in range(0, length / 2):
length = length - 1
hold = data_set[i]
data_set[i] = data_set[length]
data_set[length] = hold
return data_set
here we actually reverse in half the iterations and we memoize the value of the index we are changing so we can set the "reversal" in the same step.
word_reversed = ''
for i in range(len(word) -1, -1, -1):
word_reversed += word[i]
That will reverse a string of unknown length called (word) and call it (word_reversed).
I am using it to check to see if a word is a palindrome and I'm not allowed to use .reverse or word[::-1].
# Tyler G
# April 10, 2018
# Palindromes
import re
word = input("Palindromes are words that spell the same thing forwars or backwards, enter a word and see if its one!: ")
word = word.lower()
word = re.sub("[^a-zA-Z]+", "", word)
# count the number of characters in the string
# make a if <string>[0] = <string[counted char -1]
# repeat that over and over
# make a else print("not a ...")
word_reversed = ''
for i in range(len(word) - 1, -1, -1):
word_reversed += word[i]
itis = 0
if len(word) > 12:
print("It is not a Palindrome")
else:
for i in range(0, len(word)):
if word[i] == word_reversed[i]:
itis = itis + 1
else:
itis = itis - len(word)
if itis > 0:
print("It is a palindrome")
else:
print("It is NOT a Palindrome")
itis = 0
if len(word) > 12:
print("It is not a Palindrome")
There are two simple ways to solve the problem :
First using a temp variable :
maList = [2,5,67,8,99,34]
halfLen = len(maList) // 2
for index in range(halfLen):
temp = maList[index]
maList[index] = maList[len(maList) - 1 - index]
maList[len(maList) - 1 - index] = temp
print(maList)
Second is using a new list where to store the reversed values :
newList = []
for index,value in enumerate(maList):
newList.append(maList[len(maList) - 1 - index])
print(newList)
Something like this should work:
mylist = [1,2,3,4,5]
def reverse(orig_list):
data_list = orig_list[:]
length = len(data_list)
for i in xrange(0, length/2):
tmp = data_list[length-i-1]
data_list[length-i-1] = data_list[i]
data_list[i] = tmp
return data_list
reverse(mylist)
mylist
There are two ways of doing this
Pythonic Swap:
def Reverse_Function(item):
for i in range(len(item)/2):
item[i], item[-(i+1)] = item[-(i+1)], item[i]
return item
or
XOR application:
def Reverse_Function(item):
for i in range(len(item)/2):
item[i] = item[i] ^ item[-(i+1)]
item[-(i+1)] = item[i] ^ item[-(i+1)]
item[i] = item[i] ^ item[-(i+1)]
return item
Not looking to create a new list to hold your "temp" data?
Its simple if you look at the pattern:
reverse_list([a, b, c, d, e]) => [e, d, c, b, a]
This means that position 0 -> n, 1 -> (n - 1), 2 -> (n - 2). Which means that you can pop the last item and put it in the current index...
def reverse_list(list):
i = 0
while i < (len(list) - 1):
list.insert(i, list.pop())
i += 1
from array import *
x=array('i',[1,2,3,98,5,6,7,8])
y=array('i',[])
for i in x:
y.insert(0,i)
print(y)
Normally you can use either reversed() in general, or [::-1] on sequences. Since you are trying to implement a function from scratch, consider using stack, which is a common Abstract Data Type.
Given
import queue
s = "abc"
lst = list(s)
iter_ = iter(s)
Code
The following works on sequences and iterators similar to reversed():
def reversed_iter(iterable):
"""Yield items in reverse with a queue."""
stack = queue.LifoQueue() # 1
for x in iterable: # 2
stack.put(x)
while not stack.empty(): # 3
yield stack.get()
Demo
list(reversed_iter(s))
# ['c', 'b', 'a']
list(reversed_iter(lst))
# ['c', 'b', 'a']
list(reversed_iter(iter_))
# ['c', 'b', 'a']
Details
A stack is type of queue where the last element placed in is the first element removed. Imagine placing elements on top of each other in a container.
↓ ↑
| "c" |
| "b" |
| "a" |
-----------
Here the last element placed (enqueued) is the first to remove (dequeue), e.g. "c".
In the code, we:
Initialize a stack (the same as a LifoQueue)
Enqueue items to the stack
Dequeue items from the stack
The result are elements yielded in reversed order.
A queue is not required. Alternatively, we can emulate a stack with a simple list:
def reversed_iter(iterable):
"""Yield items in reverse with a list."""
stack = []
for x in iterable:
stack.append(x)
while stack:
yield stack.pop()
list(reversed_iter(s))
# ['c', 'b', 'a']
Emulating the Abstract Data Type is all that is needed in both examples.
list = [23, 3, 33, 44, 55, 16, 27 ];
1.more codes
def conver(list):
list.sort()
length = len(list)
s = length
new_list = [None]*length
for item in list:
s = s - 1
new_list[s] = item
print(new_list)
conver(list)
output:
[55, 44, 33, 27, 23, 16, 3]
2.easiest
list.sort()
list.reverse()
print(list)
output:
[55, 44, 33, 27, 23, 16, 3]
The following code demonstrates how to accept input:
a=[]
n=int(input("enter the no.of elements ")
for I in range(0,n):
a.insert(I,int(input(" enter elements: ")))
def rev_list(a):
print(a[::-1])
rev_list(a)
def reverseOrder():
my_list = []
a = input("What is your word?")
count = len(a) - 1
x = ''
for i in range(0, len(a)):
my_list.append(a[count])
count = count - 1
x = x.join(my_list)
print(x)
reverseOrder()
This is what I did. First I made an empty list. Then I stored users answer in a variable and subtracted one for indexing purposes. Now start a for loop and you set 'range' from 0 , len(a) then you append into the list backwards since count is = len(a). After that you set the .join function and join the list together. This is why there was variable x before the for loop.

Categories