Indexing with loops in python - python

The question asks to write a function that takes in some unordered list and from that unordered list pull any sublist that is ordered. Example [5, 2, 1, 3, 4] would become [1,3,4]. My issue is in indexing, I want a way to express that if the current iteration is greater than the one before it, I want to add it. However, the problem that my code runs into is that it compares the first and last of an item.
For my code: [5, 2, 1, 3, 4] => [5,1,3,4]
def numIncreasing(lst):
ans=[]
for index in range(len(lst)):
if lst[index]>=lst[index-1]:
ans.append(lst[index])
elif lst[index]<lst[index-1] and lst[index]<lst[index+1]:
ans.append(lst[index])
else:
continue
return ans
edit: fixed the code not recognizing the start of the pattern

Here is an algorithm that finds the largest ordered sublist:
def increasing2(lst):
length = len(lst)
for size in range(length, 1, -1):
for start in range(0, length - size + 1):
curr = lst[start:start+size]
if all(x <= y for x, y in zip(curr, curr[1:])):
return curr
return None
Which behaves as follows:
>>> increasing2([5, 2, 1, 3, 4])
[1, 3, 4]
Logic of approach:
Loop over all sublists in reversed order, so the largest sublists come before the smaller sublists:
[5, 2, 1, 3, 4]
[5, 2, 1, 3]
[2, 1, 3, 4]
[5, 2, 1]
[2, 1, 3]
[1, 3, 4]
[5, 2]
[2, 1]
[1, 3]
[3, 4]
If the current list is sorted, return it. Otherwise keep going through all the sub lists in order from largest to smallest length. This ensures that you get the largest sublist first. The algorithm also only considers sublists of length 2 or greater.

Related

Iterate through a nested list and pick certain elements and create a new list

An example:
list = [[2, 1, 2, 3, 4],
[0, 4, 5],
[1, 8, 9]]
So the first index inside a nested list decides which following numbers will be put into an unnested list.
[2, 1, 2, 3, 4] -> 2: so 1 and 2 gets picked up
[0, 4, 5] -> 0: no number gets picked up
[1, 8, 9] -> 1; number 8 gets picked up
Output would be:
[1, 2, 8]
This is what I have so far:
def nested_list(numbers):
if isinstance(numbers[0], list):
if numbers[0][0] > 0:
nested_list(numbers[0][1:numbers[0][0] + 1])
else:
numbers = list(numbers[0])
return numbers + nested_list(numbers[1:])
I try to get the list through recursion but something is wrong. What am I missing or could this be done even without recursion ?
You try using list comprehension with tuple unpacking here.
[val for idx, *rem in lst for val in rem[:idx]]
# [1, 2, 8]
NB This solution assumes you would always have a sub-list of size 1 or greater. We can filter out empty sub-lists using filter(None, lst)
list1=[[2, 1, 2, 3, 4],
[0, 4, 5],
[1, 8, 9]]
list2= []
for nested_list in list1:
for i in range(nested_list[0]):
list2.append(nested_list[i+1])
You can try List-comprehension:
>>> [sub[i] for sub in lst for i in range(1, sub[0]+1) ]
[1, 2, 8]
PS: The solution expects each sublist to be a non-empty list, else it will throw IndexError exception due to sub[0].
Another list comprehension
sum([x[1:x[0] + 1] for x in arr], [])
# [1, 2, 8]
Using builtin function map to apply the picking function, and using itertools.chain to flatten the resulting list of list:
def pick(l):
return l[1:1+l[0]]
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9]]
print( list(map(pick, ll)) )
# [[1, 2], [], [8]]
print( list(itertools.chain.from_iterable((map(pick, ll)))) )
# [1, 2, 8]
Or alternatively, with a list comprehension:
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9]]
print( [x for l in ll for x in l[1:1+l[0]]] )
# [1, 2, 8]
Two important notes:
I've renamed your list of lists ll rather than list. This is because list is already the name of the builtin class list in python. Shadowing the name of a builtin is very dangerous and can have unexpected consequences. I strongly advise you never to use the name of a builtin, when naming your own variables.
For both solutions above, the error-handling behaves the same: exception IndexError will be raised if one of the sublists is empty (because we need to access the first element to know how many elements to pick, so an error is raised if there is no first element). However, no exception will be raised if there are not enough elements in one of the sublists. For instance, if one of the sublists is [12, 3, 4], then both solutions above will silently pick the two elements 3 and 4, even though they were asked to pick 12 elements and not just 2. If you want an exception to be raised for this situation, you can modify function pick in the first solution:
def pick(l):
if len(l) == 0 or len(l) <= l[0]:
raise ValueError('in function pick: two few elements in sublist {}'.format(l))
return l[1:1+l[0]]
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9], [12, 3, 4]]
print( [x for l in ll for x in l[1:1+l[0]]] )
# [1, 2, 8, 3, 4]
print( [x for l in ll for x in pick(l)] )
# ValueError: in function pick: two few elements in sublist [12, 3, 4]

How to iterate over two lists with repeating elements

The list_1 have repeated values that are repeated in list_2 I need to find the elements that match from list_1 and list_2 and save them in result_list_1 and result_list_2 respectively and also the ones that do not match and save them in not_equals_list_1 and not_equals_list_2. The result of the items that match must also match in position.
Note: I need to use only arrays and not sets.
list_1 = [1, 5, 2, 3, 2, 4, 1, 3]
list_2 = [1, 2, 2, 3, 1, 6]
It should result in:
result_list_1 = [1, 2, 3, 2, 1]
result_list_2 = [1, 2, 3, 2, 1]
not_equals_list_1 = [3, 4, 5]
not_equals_list_2 = [6]
Your help would be of great help to me.
My method would decrease your time complexity at the cost of space complexity.
def taketwoarray(arr1,arr2):
my_dict={}
for i in arr1:# add all arr1 elements to dict
my_dict[i]=0
for i in arr2:# match if they are in arr1 increase count else set to -1
try:
if my_dict[i]!=-1:
my_dict[i]+=1
except:
my_dict[i]=-1
#now you have count of numbers that came in both lists and the numbers themselves
#and the numbers that dont match in either would be marked by zero and -1 count.
# count is set to -1 to avoid numbers coming again and again only in arr2.
Let me know if there is any issue!
collections.Counter is invaluable for things like this - like a set but with multiplicity. The algorithm is simple - we make a Counter for each list, and then for the opposite list we just check whether each element is in the other list's counter. If so, remove one occurrence and put it in the result_list. Otherwise, put it in the not_equals_list.
from collections import Counter
list_1 = [1, 5, 2, 3, 2, 4, 1, 3]
list_2 = [1, 2, 2, 3, 1, 6]
counter_1 = Counter(list_1)
counter_2 = Counter(list_2)
result_list_1 = []
result_list_2 = []
not_equals_list_1 = []
not_equals_list_2 = []
for item in list_1:
if counter_2[item] > 0:
result_list_1.append(item)
counter_2[item] -= 1
else:
not_equals_list_1.append(item)
for item in list_2:
if counter_1[item] > 0:
result_list_2.append(item)
counter_1[item] -= 1
else:
not_equals_list_2.append(item)
print(result_list_1) # [1, 2, 3, 2, 1]
print(result_list_2) # [1, 2, 2, 3, 1]
print(not_equals_list_1) # [5, 4, 3]
print(not_equals_list_2) # [6]
Order is preserved in not_equals_list from the order of the first list. If you desire it differently, you can use reversed() where necessary to change either order of iteration or to simply flip the result.
If using this type of solution with custom objects, you'll need to make sure that __hash__() is properly implemented for equality checking - since both sets and dicts are hashtable-based, and Counter is just a subclass of dict.
From a quick google search, multiset might provide a more efficient way of doing this by just converting your lists into sets and then doing set operations on them. I haven't tested this, though.
result_list_1 = []
result_list_2 = []
not_equals_list_1 = []
not_equals_list_2 = []
for item in list_1:
if item in list_2:
result_list_1.append(item)
else:
not_equals_list_1.append(item)
for item in list_2:
if item in list_1:
result_list_2.append(item)
else:
not_equals_list_2.append(item)
from collections import Counter
list_1 = [1, 5, 2, 3, 2, 4, 1, 3]
list_2 = [1, 2, 2, 3, 1, 6]
c1 = Counter(list_1)
c2 = Counter(list_2)
result_list_1 = [*(c1 & c2).elements()]
result_list_2 = result_list_1[:]
not_equals_list_1 = [*(c1 - c2).elements()]
not_equals_list_2 = [*(c2 - c1).elements()]
print(result_list_1) # [1, 1, 2, 2, 3]
print(result_list_2) # [1, 1, 2, 2, 3]
print(not_equals_list_1) # [5, 3, 4]
print(not_equals_list_2) # [6]

Python list comprehension mirroring values

I tried to figure out the following problem through list comprehension but I couldn't make it work. I will show you how I solved the problem using a loop and a list comprehension.
So, I have a list that can have 0 to 6 elements in a range(6) and when I apply my function on it I want to change the values, as shown here:
l = [0, 1, 2, 3, 4, 5]
mirror = [5, 4, 3, 2, 1, 0]
I don't want to just rotate the array by 180 degrees but I actually want to replace the values. For example, my list looks like this now:
l = [2, 5]
Then l_inverted list should look like this:
l_inverted = [3, 0]
I came up with a regular way to solve it but ever since I started learning Python I've preferred list comprehensions.
l = [0, 3, 5]
mirror = [5, 4, 3, 2, 1, 0]
i = 0
for element in l:
l[i] = mirror[element]
i += 1
This actually inverts the l list. Here's my approach using a list comprehension:
l = [3, 5]
mirror = [5, 4, 3, 2, 1, 0]
for element in l:
print(element)
l = [mirror[element] if x==element else x for x in l]
This works fine.
Until:
l = [0, 3, 5]
mirror = [5, 4, 3, 2, 1, 0]
for element in l:
print(element)
l = [mirror[element] if x==element else x for x in l]
So it will replace 5 with 0, 2 with 3 and both 5s (the new one too) become 0. Obviously, I don't want it like that.
Should I stick to the working solution or is there a smooth way to solve it with list comprehensions? I'm trying to practice list comprehensions at all times but it's not fully in my brain yet. Thanks a lot.
If you want it as a list comprehension:
>> l = [0, 3, 5]
>> mirror = [5, 4, 3, 2, 1, 0]
>> l_inverted = [mirror[x] for x in l]
>> l_inverted
[5, 2, 0]
You are drowning in a spoonful of water and trying to take us with you.
You are using bad naming conventions that make your simple problem complicated to comprehend.
orig = [0, 1, 2, 3, 4, 5]
orig_rev = l[::-1]
selector = [0, 3, 5]
result = [orig_rev[i] for i in selector]
print(result ) # [5, 2, 0]
Based on your first two examples, it seems you are looking for the complement of each list value according to some base, similar to ones' complement. You could either hard-code the base as 5 or whatever, or you could assume it's the maximum number in the list and calculate it. Here's a solution for the latter:
Concept
<this complement> = <max value in list> - <this value>
Code
values = [0, 3, 2, 4, 5]
max_value = max(values)
complements = [max_value - value for value in values]
print complements
Result
[5, 2, 3, 1, 0]

Creating a new list when before it reaches a number

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.

remove minimum items from two list to make lists same

I have two lists with same items and different orders.
for example:
a = [4, 2, 3, 1]
b = [2, 3, 1, 4]
Which item(s) should I remove to make lists same?
Here: [4] is an answer, so:
a = [2, 3, 1]
b = [2, 3, 1]
But [2, 4] or [2, 3, 1] are also answers, if I remove [2, 3, 1]:
a = [4]
b = [4]
I need to remove the minimum number of elements, here [4] is the optimal solution.
Another example:
a = [1, 2, 3, 4]
b = [2, 1, 4, 3]
Possible answers:
[1, 3]
[1, 4]
[2, 3]
[2, 4]
Order of algorithm is unimportant.
I would certainly search for the longest common subsequence first (google LCS, many algorithms are available, e.g. on algorithmist), then if you remove the LCS elements from one of the original list you got the shortest list of elements to remove. In pseudocode :
lcs = LCS(a,b)
res = copy(a)
foreach element e in lcs
remove(res,e)
return res

Categories

Resources