How do I reverse a list using while loop? - python

Input list: [1, 2, 3, 4, 5]
Output: [5, 4, 3, 2, 1]
I know how to do it with for loop, but my assignment is to do it with while loop; which I have no idea to do. Here is the code I have so far:
def while_version(items):
a = 0
b = len(items)
r_list = []
while (a!=b):
items[a:a] = r_list[(-a)-1]
a+=1
return items

I would say to make the while loop act like a for loop.
firstList = [1,2,3]
secondList=[]
counter = len(firstList)-1
while counter >= 0:
secondList.append(firstList[counter])
counter -= 1

The simplest way would be:
def while_version(items):
new_list = []
while items: # i.e. until it's an empty list
new_list.append(items.pop(-1))
return new_list
This will reverse the list:
>>> l1 = [1, 2, 3]
>>> l2 = while_version(l)
>>> l2
[3, 2, 1]
Note, however, that it also empties the original list:
>>> l1
[]
To avoid this, call e.g. l2 = while_version(l1[:]).

The trivial answer
Given
a = [1, 2, 3, 4, 5]
then
a[::-1]
returns
[5, 4, 3, 2, 1]
In your code:
You use r_list[(-a)+1], buy you have never assigned r_list any value (just "r_list = []")
I think your are confusing "items" with "r_list". So I think you want to return "r_list" instead of "items" (the input parameter)
The assignment should be "r_list[a] = items[-a-1]", but that doesn't work. You should use "r_list.append(items[-a-1])"
The return should be "return r_list"
"while (a!=b)" should be "while (a < b)" for readeability
Hope this helps

Related

Add number to sorted list recursively

I want to add my_num to where it fits in the given list and I want to solve it by using this structure, however I'm not getting the right results. What can I do?
my_lst = [1, 3, 5]
my_num = 4
def new_number(lst):
return lst
def plus_number(n, lst):
def add_number(n2, lst2):
for i in range(len(lst)):
if lst2[0] > n2:
return [n2] + lst2
else:
return [lst[0]] + add_number(n2, lst2[1:])
return new_number(add_number(n, lst))
result = plus_number(my_num, my_lst)
print(result)
>>> [1, 1, 4, 5]
Edit: changed [lst[0]] to [lst2[0]] and it works. Thanks!
There is already a stdlib method for this, no need to reinvent it:
>>> from bisect import insort
>>> my_lst = [1, 3, 5]
>>> insort(my_lst, 4)
>>> my_lst
[1, 3, 4, 5]
I've two solutions:
Using a sentinel, enumerate and insert.
my_lst = [1, 3, 5]
my_num = 4
my_lst.append(my_num) # sentinel
for i, value in enumerate(my_lst):
if my_num <= value:
my_lst.insert(i, my_num)
break
my_lst.pop() # remove sentinel
print(my_lst)
Using enumerate, insert and else:
my_lst = [1, 3, 5]
my_num = 4
for i, value in enumerate(my_lst):
if my_num <= value:
my_lst.insert(i, my_num)
break
else:
my_lst.append(my_num)
print(my_lst)
If you can't use break, you can circumvent that with a function. Note that this will be trickier for the first function.

how to combine same matching items in a 1d list and make it as 2d list [duplicate]

From this list:
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
I'm trying to create:
L = [[1],[2,2],[3,3,3],[4,4,4,4],[5,5,5,5,5]]
Any value which is found to be the same is grouped into it's own sublist.
Here is my attempt so far, I'm thinking I should use a while loop?
global n
n = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5] #Sorted list
l = [] #Empty list to append values to
def compare(val):
""" This function receives index values
from the n list (n[0] etc) """
global valin
valin = val
global count
count = 0
for i in xrange(len(n)):
if valin == n[count]: # If the input value i.e. n[x] == n[iteration]
temp = valin, n[count]
l.append(temp) #append the values to a new list
count +=1
else:
count +=1
for x in xrange (len(n)):
compare(n[x]) #pass the n[x] to compare function
Use itertools.groupby:
from itertools import groupby
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
print([list(j) for i, j in groupby(N)])
Output:
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
Side note: Prevent from using global variable when you don't need to.
Someone mentions for N=[1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 1] it will get [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5], [1]]
In other words, when numbers of the list isn't in order or it is a mess list, it's not available.
So I have better answer to solve this problem.
from collections import Counter
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
C = Counter(N)
print [ [k,]*v for k,v in C.items()]
You can use itertools.groupby along with a list comprehension
>>> l = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
>>> [list(v) for k,v in itertools.groupby(l)]
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
This can be assigned to the variable L as in
L = [list(v) for k,v in itertools.groupby(l)]
You're overcomplicating this.
What you want to do is: for each value, if it's the same as the last value, just append it to the list of last values; otherwise, create a new list. You can translate that English directly to Python:
new_list = []
for value in old_list:
if new_list and new_list[-1][0] == value:
new_list[-1].append(value)
else:
new_list.append([value])
There are even simpler ways to do this if you're willing to get a bit more abstract, e.g., by using the grouping functions in itertools. But this should be easy to understand.
If you really need to do this with a while loop, you can translate any for loop into a while loop like this:
for value in iterable:
do_stuff(value)
iterator = iter(iterable)
while True:
try:
value = next(iterator)
except StopIteration:
break
do_stuff(value)
Or, if you know the iterable is a sequence, you can use a slightly simpler while loop:
index = 0
while index < len(sequence):
value = sequence[index]
do_stuff(value)
index += 1
But both of these make your code less readable, less Pythonic, more complicated, less efficient, easier to get wrong, etc.
You can do that using numpy too:
import numpy as np
N = np.array([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
counter = np.arange(1, np.alen(N))
L = np.split(N, counter[N[1:]!=N[:-1]])
The advantage of this method is when you have another list which is related to N and you want to split it in the same way.
Another slightly different solution that doesn't rely on itertools:
#!/usr/bin/env python
def group(items):
"""
groups a sorted list of integers into sublists based on the integer key
"""
if len(items) == 0:
return []
grouped_items = []
prev_item, rest_items = items[0], items[1:]
subgroup = [prev_item]
for item in rest_items:
if item != prev_item:
grouped_items.append(subgroup)
subgroup = []
subgroup.append(item)
prev_item = item
grouped_items.append(subgroup)
return grouped_items
print group([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
# [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]

Writing an implementation of Selection Sort in Python and my code is throwing "min() arg is an empty set" on a populated list

As an exercise, I am writing an implementation of selection sort (as well as insertion sort and quick sort); for me, the best way to gain a deeper understanding of a function is implementing it myself. I have the following code:
def selectionSort(L):
S = []
i = 0
a = len(L)
while i <= a:
S.append(min(L)) #Find min(L) and add it to set S
L.remove(min(L)) #Remove that same element min(L) from L
i += 1
return S #Return the sorted list
L = [int(x) for x in input('Input a list to be sorted: ').split()]
print(selectionSort(L))
The idea here is to have the user input a list of integers to be sorted and run the selectionSort() function on the list. For the life of me, I cannot figure out why min(L) is throwing the error min() arg is an empty sequence. I have written other code which takes a list as input in the same manner and it works fine.
You are looping once too many:
i = 0
a = len(L)
while i <= a:
#
i += 1
Here i goes from 0 through to len(a) inclusive, so you loop len(a) + 1 times. But there are only len(a) items in the list.
Your options are to pick one of the following
start i at 1, not at 0
stop at i < a (dropping the =) to not include len(a)
Use a for i in range(len(a)) loop; this produces values from 0 through to len(a) - 1.
Simply test if L is empty with
while L:
and remove a and i from your code altogether.
The latter option leads to less code and is clearer:
def selectionSort(L):
S = []
while L:
pick = min(L)
S.append(pick)
L.remove(pick)
return S #Return the sorted list
Note that I store the result of min(L) first to avoid scanning the list twice.
Your issue should already be fixed by #AChampion's comment, I just want to add some clarifications about what you're trying to do. The following code should work for you, but it still has some other issues:
def selectionSort(L):
S = []
i = 0
a = len(L)
while i < a: # Or even better: for i in range(len(L)) and without i += 1
S.append(min(L)) #Find min(L) and add it to set S
L.remove(min(L)) #Remove that same element min(L) from L
i += 1
return S
But, look at the following outputs:
>>> my_list = [3, 5, 1, 9, 2, 7, 4, 7, 4]
>>> selectionSort(my_list)
[1, 2, 3, 4, 4, 5, 7, 7, 9]
>>>
>>> my_list
[]
You see, the input my_list is empty now, because it is altered inside your function. To fix this issue, you can do: (because you may still need to use your original list)
def my_sort(my_list):
temp = my_list[:]
sorted_list = []
for i in range(len(temp)):
sorted_list.append(min(temp))
temp.remove(min(temp))
return sorted_list
Now, inside our function, we use a copy of our original list temp = my_list[:], this is equivalent to temp = copy.copy(my_list):
>>> import copy
>>>
>>> my_list = [3, 5, 1, 9, 2, 7, 4, 7, 4]
>>> id(my_list)
36716416
>>> l1 = my_list
>>> id(l1)
36716416 # In this case the id of l1 is the same as my_list's id!
>>> l2 = my_list[:]
>>> id(l2)
36738832 # This is NOT the same as my_list's id
>>> l3 = copy.copy(my_list)
>>> id(l3)
36766424 # This is NOT the same as my_list's id
Output of the new function:
>>> my_list = [3, 5, 1, 9, 2, 7, 4, 7, 4]
>>> my_sort(my_list)
[1, 2, 3, 4, 4, 5, 7, 7, 9]
>>>
>>> my_list
[3, 5, 1, 9, 2, 7, 4, 7, 4] # Our original list is still alive!
You may need:
As per your goal (implementing your own function for learning purpose), you may also need to use your own min function instead of the built-in one, the following is an example that you can try:
def my_min(my_list):
min = my_list[0]
for i in range(1, len(my_list)):
if min > my_list[i]:
min = my_list[i]
return min
Output:
>>> l1 = [3, 7, 2, 5]
>>> my_min(l1)
2

Why does `mylist[:] = reversed(mylist)` work?

The following reverses a list "in-place" and works in Python 2 and 3:
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[:] = reversed(mylist)
>>> mylist
[5, 4, 3, 2, 1]
Why/how? Since reversed gives me an iterator and doesn't copy the list beforehand, and since [:]= replaces "in-place", I am surprised. And the following, also using reversed, breaks as expected:
>>> mylist = [1, 2, 3, 4, 5]
>>> for i, item in enumerate(reversed(mylist)):
mylist[i] = item
>>> mylist
[5, 4, 3, 4, 5]
Why doesn't the [:] = fail like that?
And yes, I do know mylist.reverse().
CPython list slice assigment will convert the iterable to a list first by calling PySequence_Fast. Source: https://hg.python.org/cpython/file/7556df35b913/Objects/listobject.c#l611
v_as_SF = PySequence_Fast(v, "can only assign an iterable");
Even PyPy does something similar:
def setslice__List_ANY_ANY_ANY(space, w_list, w_start, w_stop, w_iterable):
length = w_list.length()
start, stop = normalize_simple_slice(space, length, w_start, w_stop)
sequence_w = space.listview(w_iterable)
w_other = W_ListObject(space, sequence_w)
w_list.setslice(start, 1, stop-start, w_other)
Here space.listview will call ObjSpace.unpackiterable to unpack the iterable which in turn returns a list.

Getting lists where an element passes through every index in python?

Kind of a strange request.
Let's say I have the following list:
[1,2,3]
And I want something, say, the number 9, to pass through every index, to get the following list of lists:
[[9,1,2,3],
[1,9,2,3],
[1,2,9,3],
[1,2,3,9]]
Any idea how to do this easily? Also, is there a name for this sort of thing?
Edit: I realize I can do something like the following:
lists=[]
for i in range(4):
new_list = [1,2,3]
new_list.insert(i,9)
lists+=[new_list]
but I consider this inelegant. Thoughts?
You could do something like
l = [1,2,3]
new_l = [l[:i] + [9] + l[i:] for i in range(len(l) + 1)]
How about a for loop:
l = [1,2,3]
res = []
for i in xrange(len(l)+1):
l2 = l[:]
l2.insert(i,9)
res.append(l2)
Here is another thing I thought of:
l = [1,2,3]
q = [l[:] for _ in range(len(l)+1)]
map(lambda(x):x.insert(q.index(x),9), q)
Then q will contain your list:
print q
[9, 1, 2, 3], [1, 9, 2, 3], [1, 2, 9, 3], [1, 2, 3, 9]]
enumerate and repeat are your friends.
from itertools import repeat
some_list = [1, 2, 3]
new_lists = []
new_elt = 9
for i, lst in enumerate(L[:] for L in repeat(some_list, len(some_list)+1))
lst.insert(i, new_elt)
new_lists.append(lst)
print new_lists

Categories