Related
I want to remove a number by an x amount of times from a list. For example, for this list [1, 4, 3, 6, 4, 3, 2, 4, 8, 11] if I wanted to remove the integer 4 by 2 times, the list would look like this: [1, 3, 6, 3, 2, 4, 8, 11]. What code would be efficient in this situation?
The code I have in the moment:
int_list = [1, 4, 3, 6, 4, 3, 2, 4, 8, 11]
result = []
remove = int(input('remove: '))
times = int(input('times: '))
for i in int_list:
if i != remove:
result.append(i)
elements = result
print(elements)
Working in-place
The list method remove removes the first occurrence of the argument from the list. So you can simply call it x amount of times on the list:
int_list = [1, 4, 3, 6, 4, 3, 2, 4, 8, 11]
remove = 4
times = 2
for _ in range(times):
int_list.remove(remove)
print(int_list)
Will give:
[1, 3, 6, 3, 2, 4, 8, 11]
Handling errors
In case the element is not found, remove will raise an error. If you want to avoid that:
Check the input pre-hand by making sure there are enough elements to remove using the count method:
if int_list.count(remove) <= times:
# rest of code
If you want to just remove all possible elements, add a check before the remove call:
for _ in range(times):
if remove in int_list:
int_list.remove(remove)
Returning a new list
You can of course simply create a copy of the input list (res = int_list.copy()) and then apply the in-place solution from above. Otherwise, to create a completely new list, use times as a counter for how many times to "skip" the wanted number:
int_list = [1, 4, 3, 6, 4, 3, 2, 4, 8, 11]
remove = 4
times = 2
res = []
for num in int_list:
if num == remove and times > 0:
times -= 1
continue
res.append(num)
print(res)
In this case there is no error handling to make. We are simply ignoring the required element a given amount of times. If it doesn't exist at all, less, or more times than given - nothing will happen.
I have a list and I want to iterate through it from element 1 to the end, and then finish up with element 0. Ie, essentially it's basic list iteration, except the very first element should be at the end.
I could do this with for i in range(len(myList)): and then adding 1 to i and checking for the edge case (that 's how I'd do in it C++), but was wondering if Python had some syntax which would make this concept easier to express?
I don't believe there is a convenient syntax for this, but you could easily define your own: I've chosen the name irotate by analogy to itertools.islice, and written it in a way such that it can be provided a single-use iterable such as a generator expression.
def irotate(iterable, k=1):
iterator = iter(iterable)
initial_elements = [next(iterator) for _ in range(k)]
yield from iterator
yield from initial_elements
Example usage:
>>> list(irotate(range(5)))
[1, 2, 3, 4, 0]
>>> for x in irotate(y**2 for y in range(5)): print(x)
...
1
4
9
16
0
The most simple way to achieve this, without the need for error handling/checks is to just use slicing.
_list = [1, 2, 3, 4]
for i in _list[1:] + _list[:1]:
print(i)
#2
#3
#4
#1
One could use modulo to get something like this -
mylist = [i for i in range(8)]
for i in range(5):
diff = i
rotated = [mylist[i % len(mylist)] for i in range(diff, len(mylist)+diff)]
print(rotated)
Output is -
[0, 1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 0]
[2, 3, 4, 5, 6, 7, 0, 1]
[3, 4, 5, 6, 7, 0, 1, 2]
[4, 5, 6, 7, 0, 1, 2, 3]
It is creating another list. So, may wanna keep that in mind.
... and then adding 1 to i and checking for the edge case ...
If you want only get rid of the checking for the edge case, use cycle() from the itertools standard module to create an infinite iterator:
import itertools
my_list = [2, 10, "a"]
my_iter = itertools.cycle(my_list) # 2, 10, "a", 2, 10, "a", 2, 10, "a", ...
next(my_iter) # drop the first element
for __ in range(len(my_list)):
curr_elem = next(my_iter)
print(curr_elem) # or do other activity with the current element
The output:
10
a
2
Working on some example questions, the particular one asks to make a function which would take a list and return a new one which would make every ascending sublist in the list go in descending order and leave the descending sublists as they are. For example, given the list [1,2,3,4,5], I need the list [5,4,3,2,1] or given a list like [1,2,3,5,4,6,7,9,8] would return [5,3,2,1,9,7,6,4,8]
Here's what I have so far, but it does not do anything close to what I'd like it to do:
def example3(items):
sublst = list()
for i in items:
current_element = [i]
next_element = [i+1]
if next_element > current_element:
sublst = items.reverse()
else:
return items
return sublst
print (example3([1,2,3,2])) #[[1, 2, 3, 2], [1, 2, 3, 2], [1, 2, 3, 2], [1, 2, 3, 2]]
EDIT:
I feel like people are a little confused as to what I want to do in this case, heres a better example of what I'd like my function to do. Given a list like: [5, 7, 10, 4, 2, 7, 8, 1, 3] I would like it to return [10, 7, 5, 4, 8, 7, 2, 3, 1]. As you can see all the sublists that are in descending order such as ([5,7,10]) gets reversed to [10, 7, 5].
It was a bit challenging to figure out what you need.
I think you want something like as follows:
import random
l = [5, 7, 10, 4, 2, 7, 8, 1, 3]
bl =[]
while True:
if len(l) == 0:
break
r = random.randint(0, len(l))
bl.extend(l[r:None:-1])
l = l[r+1:]
print(bl)
Out1:
[10, 7, 5, 4, 8, 7, 2, 3, 1]
Out2:
[10, 7, 5, 2, 4, 1, 8, 7, 3]
Out3:
[3, 1, 8, 7, 2, 4, 10, 7, 5]
Out4:
[2, 4, 10, 7, 5, 3, 1, 8, 7]
etc.
If you want a specific reverse random list:
import random
loop_number = 0
while True:
l = [5, 7, 10, 4, 2, 7, 8, 1, 3]
bl =[]
while True:
if len(l) == 0:
break
r = random.randint(0, len(l))
bl.extend(l[r:None:-1])
l = l[r+1:]
loop_number += 1
if bl == [10, 7, 5, 4, 8, 7, 2, 3, 1]:
print(bl)
print("I tried {} times".format(loop_number))
break
Out:
[10, 7, 5, 4, 8, 7, 2, 3, 1]
I tried 336 times
The general algorithm is to keep track of the current ascending sublist you are processing using 2 pointers, perhaps a "start" and "curr" pointer. curr iterates over each element of the list. As long as the current element is greater than the previous element, you have an ascending sublist, and you move curr to the next number. If the curr number is less than the previous number, you know your ascending sublist has ended, so you collect all numbers from start to curr - 1 (because array[curr] is less than array[curr - 1] so it can't be part of the ascending sublist) and reverse them. You then set start = curr before incrementing curr.
You will have to deal with the details of the most efficient way of reversing them, as well as the edge cases with the pointers like what should the initial value of start be, as well as how to deal with the case that the current ascending sublist extends past the end of the array. But the above paragraph should be sufficient in getting you to think in the right direction.
I have a list of lists. I want to collect all of the sublists with the same first element, combine their elements, and remove duplicates. I'm getting close, but my current solution dies on an index error.
Current output:
[[1, 2, 3, 7, 8, 9], [5, 5, 8], [2, 2, 0, 2, 3, 4, 5, 6]]
Correct output:
[[1, 2, 3, 7, 8, 9], [4, 5, 6, 3, 2], [5, 5, 8], [2, 2, 0]] (not necessarily in that order)
Code:
listoflist = [[1,2,3,1],[4,5,6],[1,7,8],[4,3,2],[5,5,8],[2,2,0],[1,9,9]]
try: #(try and except is to deal with when lists are removed)
for i in range(len(listoflist)): #iterating forwards
x = listoflist[i][0] #indexing through sublists and setting the first value
for j in range(len(listoflist)-1,0,-1): #iterating backwards
if x == listoflist[j][0] and listoflist[i] != listoflist[j]: #comparing
listoflist[i].extend(listoflist[j]) #combining lists and removing the old list
listoflist.remove(listoflist[j])
new = set(listoflist[i]) #conversion to set and back to list
listoflist[i] = list()
for obj in new:
listoflist[i].append(obj)
print listoflist[i]
print listoflist
except IndexError:
print listoflist
ANALYSIS
Your main problem is that you violate a basic practice: do not change an iterable while you're iterating on it. Your outer loop runs i through values 0-5. However, by the time you get to 4, you no longer have an item #4 in the list. I did some simple tracing on your code, including a better message on error:
except IndexError:
print "ERROR", i, j, listoflist
Output:
ERROR 4 1 [[1, 2, 3, 1, 1, 9, 9, 1, 7, 8], [4, 5, 6, 4, 3, 2], [5, 5, 8], [2, 2, 0]]
REPAIR
Don't alter the original list. Instead, build a new list from the old one. Gather all of the lists that begin with the same element; append the resulting set to the result you want.
listoflist = [[1,2,3,1],[4,5,6],[1,7,8],[4,3,2],[5,5,8],[2,2,0],[1,9,9]]
result = []
try:
for i in range(len(listoflist)):
x = listoflist[i][0]
if x < 0: # Skip any sub-list already used
continue
gather = listoflist[i][:] # Copy list for processing
for j in range(len(listoflist)-1,0,-1):
if x == listoflist[j][0] and i != j:
gather.extend(listoflist[j])
listoflist[j][0] = -j # Uniquely mark this as not in use.
print i, j, gather
result.append(list(set(gather)))
print i, result
print result
except IndexError:
print "ERROR", i, j, listoflist
Yes, this is still much longer than needed; if you really want to dig into Python, you can group the lists by first element and do all the processing in one long line. I've tried to keep this more accessible for you.
Here is my solution. I went for building dict{} based on the first elem in the list as the key, and merging the lists based on that.
listoflist = [[1,2,3,1],[4,5,6],[1,7,8],[4,3,2],[5,5,8],[2,2,0],[1,9,9]]
temp_dict = {}
result_list = []
# build a dict{int:list[]} based on first elem of list
for ll in listoflist:
if ll[0] not in temp_dict:
temp_dict[ll[0]]=ll
else:
for x in ll:
temp_dict[ll[0]].append(x)
# now build a list of list with our dict
for key, values in temp_dict.items():
temp_list = []
temp_list.append(key)
for x in values:
if x not in temp_list:
temp_list.append(x)
result_list.append(temp_list)
print(result_list)
Output:
[[1, 2, 3, 7, 8, 9], [4, 5, 6, 3, 2], [5, 8], [2, 0]]
How do I create a new list that contains sublists of ints but the way of divide it is when the next number is the minimun (or equal to the first value founded)?
For example
List1=[1,2,3,4,5,1,2,3,4,1,2,3,4,5,6]
The output that I am looking for is shown below:
Complete_List=[[1,2,3,4,5],[1,2,3,4],[1,2,3,4,5,6]]
I tried looping through the list and appending it when the value is greater than 1 . However it will not work as it doesn't create another list inside it.
Do I have to right a regex for this problem?
Some guidance would be really helpful.
Thank you
Here's something that will split a generic iterable on a given value.
def split_on_value(iterable, split_value):
iterator = iter(iterable)
outer, inner = [], [next(iterator)]
for value in iterator:
if value == split_value:
outer.append(inner)
inner = []
inner.append(value)
outer.append(inner)
return outer
value_list = [1, 2, 3, 4, 5, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
print split_on_value(value_list, 1)
# [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
print split_on_value(value_list, 3)
# [[1, 2], [3, 4, 5, 1, 2], [3, 4, 1, 2], [3, 4, 5, 6]]
A vanilla, straightforward, CS101 solution. Though, possibly the most efficient one, because it scans the list exactly once. It also does not assume that segments begin with 1.
fragment = []
result = []
prev = List1[0] - 1 # Preset the previous element marker
for n in List1:
if n > prev:
fragment.append(n)
else:
result.append(fragment)
fragment = [n]
prev = n
result.append(fragment)
#[[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
First you search for the 1's, or whatever your condition is, and get the indices within the list. Don't forget to append the len(list) to include the last segment.
idx = [i for i, l in enumerate(List1) if l == 1] + [len(List1)]
Optional, if you want the beginning end of the List. That is, you do not know if there will be a 1 always at index 0.
idx = [0] + idx if idx[0] != 0 else idx
Then, split the list at those indices you found.
complete_list = [List1[ind1:ind2] for ind1, ind2 in zip(idx[:-1], idx[1:])]
and the result:
[[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
You can try this to split at every instance of 1:
List1=[1,2,3,4,5,1,2,3,4,1,2,3,4,5,6]
print [map(int, list("1"+i)) for i in ''.join(map(str, List1)).split("1")][1:]
By mapping over List1 with the string function, we can then join all the numbers in the list into one large string. From there, the algorithm splits itself at each instance of one, creating a list containing the new strings of digits. from there, the code maps the integer function over a list created of the strings and appending 1 at the front of the string to make up for the lost 1 when it originally split, creating a list within a list.