Add number to sorted list recursively - python

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.

Related

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]]

Checking if you can divide list into 2 with exact sum of numbers that are in them

My problem is that I have a list, for example
l =[1, 2, 3, 4, 5, 15]
and I would like to divide it in two lists, list1 that would have a single element of the actual list which should be the sum of all other numbers in the list, and list2 containing rest. So the output for this would be ([1, 2, 3, 4, 5], [15]) if its possible if not, return False.
This is one way, though not necessarily optimal. It uses the, in my opinion underused, for...else... construct.
I've also reversed the range iterator. This is more efficient in the case you provided.
l = [1, 2, 3, 4, 5, 15]
def splitter(l):
for i in reversed(range(len(l))):
if sum(l[:i]) == sum(l[i:]):
return [l[:i], l[i:]]
else:
return False
splitter(l) # [[1, 2, 3, 4, 5], [15]]
Should it be possible for the positions of the values to change in the list? If not you can try an iteration such as:
l = [1, 2, 3, 4, 5, 15]
dividable = "False"
x = 0
while dividable == "False":
l1 = l[0:x]
l2 = l[x:len(l)]
if sum(l1) == sum(l2):
dividable = "True"
elif x == len(l):
#not possible
break
else:
x += 1
This answer should help in all cases.
No imports required and no sorting required for the data.
def split_list(l):
dividable=False
index=0
for i in range(len(l)):
if l[i]==sum(l)-l[i]:
dividable=True
index=i
break
if dividable:
l1=l[index]
l.remove(l[index])
return (l1,l)
else:
return False
Might not be the optimised way, but a better and clear way to understand for beginners.
split_list([1,2,3,4,5,15])
[15],[1,2,3,4,5]
Hope this helps. Thanks
what about this?
l =[1, 2, 3, 4, 5, 15]
l=sorted(l)
track=[]
for i in l:
track.append(i)
if sum(track) in l and len(track)==len(l[1:]):
print(track,[sum(track)])
output:
[1, 2, 3, 4, 5], [15]
You need to do a couple of steps:
1) Sort the list from small to large. (Into a new list if you don't want to alter the original)
2) Sum every other element of the list and see if it's equal.
3) If false return false
4) if true:
Store the last (biggest) value in a variable and delete this from the duplicate of the original list.
Make a second list with only that last value in it.
Create another new list and add the altered duplicate list and the list made of the biggest element.
Return the last created list.
Then you're done
Brute force:
import itertools
x = [1, 2, 3, 4, 5, 15]
for size in range(1,len(x)):
for sublist in itertools.combinations(x, size):
comp = x[:]
for n in sublist:
comp.remove(n)
if sum(comp) == sum(sublist):
print(comp, sublist)
[1, 2, 3, 4, 5] (15,)
[15] (1, 2, 3, 4, 5)
This approach can handle duplicated numbers.
Using numpy:
def split(l):
c = np.cumsum(l)
idx = np.flatnonzero(np.equal(l, c[-1] / 2.0))
return (l[:idx[0]], l[idx[0]:]) if idx.size > 0 else False
Alternatively, if using Python > 3.2:
import itertools
def split(l):
c = list(itertools.accumulate(l))
h = c[-1] / 2.0
if h in c:
i = l.index(h)
return l[:i], l[i:]
return False
Finally, if you want to use "pure" Python (no imports):
def split(l):
c = [sum(l[:k]) for k in range(1, len(l) + 1)]
h = c[-1] / 2.0
if h in c:
i = l.index(h)
return l[:i], l[i:]
return False

Making a multidimensional list flat where the sublists are equal to their average

So for example, I have the list = [1, [2, [3, [5, [5]]]]] the resulting list would be [1,3].
I currently have this,
def avg(mylist):
if mylist == []:
return mylist
elif type(mylist[0]) == list:
mylist[0] = 0 # average of mylist[0]
return mylist[:1]+avg(mylist[1:])
elif type(mylist[0]) == float:
return mylist[:1]+avg(mylist[1:])
Which works the way I want it too but I cannot find a way to set mylist[0] = the average of mylist[0]. I have also tried it a lot of different ways but I cannot find one that works.
EDIT: Another example of something I tried.
total = 0
nosublist=True
if mylist == []:
return mylist
for x in mylist:
if type(x) == list:
nosublist=False
if nosublist:
return mylist[:1]+average(mylist[1:])
elif not nosublist:
for x in mylist:
if type(x) == list:
total += average(x)
else:
total += x
mylist[0] = total/len(mylist)
return average(mylist[:1])+average(mylist[1:])
def isiter(x):
try:
iter(x)
return True
except TypeError:
return False
def _flatten(x, reduce=iter):
for i in x:
if isiter(i):
r = reduce((j for j in _flatten(i, reduce=reduce)))
if isiter(r):
yield from r
else:
yield r
else:
yield i
Now you can plugin mean
def mean(x):
l = list(x)
return sum(l)/len(l)
l = [1, [2, [3, [5, [5]]]]]
list(_flatten(l, reduce=mean))
>>> [1, 3.0]
or
mean(flatten(l, reduce=mean))
>>> 2.0
EDIT:
If you really need only a single function:
def flatten(x, reduce=iter):
return reduce(_flatten(x, reduce=reduce))
This isn't probably the best solution but you could use it help make yours even better! I created two lists on each recursive call one with only elements that aren't lists and one with elements that were all lists (just in case you had a format like [1, 3, [2], [2], [3, [5, [5, 5]]]]) and created a call stack to take the sum of the elements in each inner array and one to take the length of each inner array and then took the average. Of course since there might be multiple arrays in the list of arrays you can map this same functionality for each array and accumulate their avg's.
code
list1 = [1, [2, [3, [5, [5]]]]]
list2 = [1, [2, 3], [4, 5], [5, [3, 4]]]
def avg(mylist):
"""flattens an array where the sublists to flatten are the average of that sublist"""
subarrays = filter(lambda x: type(x) == type([]), mylist)
rootelems = filter(lambda x: type(x) != type([]), mylist)
avg_all = lambda elem: sum((avg(elem))) / len(avg(elem))
if subarrays == []:
return mylist
return rootelems + map(avg_all, subarrays)
print avg(list1)
print avg(list2)
result
[1, 3]
[1, 2, 4, 4]

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

How do I reverse a list using while loop?

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

Categories