Related
I'm trying to find the next maximum value of nested lists, I already have a nested list sorted by bubblesort, I need to take the largest element of each nested list and insert it into the solution vector, until the solution vector is sorted.
P.S: I can't delete the element from the initial nested list, only find the next maximum value.
See the image at the bottom as an example:
Nested_list = [[1, 7, 9], [4, 5, 6], [2, 3, 8], [0]]
The way I devised deleted the largest vector from the original list, which was quite time consuming, I believe that just moving the index to the next largest value will consume less time:
def bubbleSort(array):
n = len(array)-1
for i in range(n):
for j in range(0, n-i):
if array[j] > array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
else:
continue
return array
def ordena_lista(output):
for sublista in output:
bubbleSort(sublista)
def maior_valor_lista(output):
return list(el[-1] for el in output)
def nested_remove(L, x):
if x in L:
L.remove(x)
else:
for element in L:
if type(element) is list:
nested_remove(element, x)
b = list(random.sample(range(10), 10))
n= m.floor(m.sqrt(len(b)))
output=list([b[i:i + n] for i in range(0, len(b), n)])
ordena_lista(b)
while output:
valores_maximo = maior_valor_lista(output)
var = max(valores_maximo, key=int)
final = [var] + final
nested_remove(output, var)
output = list(filter(None, output))
The simplest solution would be the following,
from functools import reduce
from operator import add
def sort_nested_list(nested_list):
return sorted(reduce(add, nested_list))
but, without knowing the exact implementation details of python's sorted, I can't tell you if it takes advantage of your pre-sorting.
If we know the sublists are sorted, and we are allowed to copy the list, and we know how many elements there are in total, we can write the following,
import math
from copy import deepcopy
def get_max_and_pop(nested_list):
""" find the maximum element of a sublist of nested_list, remove it from the sublist, and return it """
print(f"get_max_and_pop says: {nested_list}")
return max(nested_list, key=lambda x: x[-1:]).pop()
def sort_nested_list_whose_sublists_are_sorted(nested_list, n_elements):
nested_list_copy = deepcopy(nested_list)
return [get_max_and_pop(nested_list=nested_list_copy) for _ in range(n_elements)][::-1]
edit: without knowledge of the number of elements, we can write,
from copy import deepcopy
def sort_nested_list_whose_sublists_are_sorted_iter(nested_list):
nested_list_copy = deepcopy(nested_list)
while any(nested_list_copy):
yield max(nested_list_copy, key=lambda x: x[-1:]).pop()
This amounts to a bizarre, woefully inefficient and completely unnecessary sorting algorithm but here goes anyway:
Nested_list = [[9, 7, 1], [4, 5, 6], [2, 3, 8], [0]]
for e in Nested_list:
e.sort()
Output_list = []
Nested_list_copy = [[e_ for e_ in e] for e in Nested_list]
element_count = sum(len(e) for e in Nested_list)
for _ in range(element_count):
m = None
for i, e in enumerate(Nested_list_copy):
if e:
tm = e[-1]
if m is None or tm > m:
m = tm
k = i
Output_list.insert(0, Nested_list_copy[k].pop())
print(Nested_list)
print(Output_list)
Output:
[[1, 7, 9], [4, 5, 6], [2, 3, 8], [0]]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
I have the following variables:
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
Now I want to concat all a, b, c, d, e to one list:
[1, 2, 3, "de", 5, 4, 5, 23, 11, 5, "dg", "kuku"]
I have tried itertools.chain but it didn't work. Please advise how can I make the concatenation?
chain works with iterables. What you mean is: concatenate these lists and raw values.
I see two steps:
def ensure_list(x):
if isinstance(x, list):
return x
return [x]
lists = map(ensure_list, (a, b, c, d, e))
concatenated = list(itertools.chain.from_iterable(lists))
You could define a function that takes an arbitrary number of arguments and iteratively constructs a list out of them depending on their type like this:
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
def concat(*args):
out = []
for arg in args:
if isinstance(arg, list):
out.extend(arg)
else:
out.append(arg)
return out
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
Alternatively you could map over the list of args, ensure they're all a list, then use itertools.chain to combine the map object like this:
def concat(*args):
return list(itertools.chain(*map(lambda x : x if isinstance(x, list) else [x], args)))
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
And here's a much more opaque way of accomplishing the same thing just for fun in a list comprehension:
def concat(*args):
return [x
for arg in args
for x in (arg if isinstance(arg, list) else [arg])]
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
You could also create a generator with map that yields either the argument or the argument in a list and then sum it all together with a list (you probably shouldn't actually do this, but it's neat).
def concat(*args):
return sum(map(lambda arg : arg if isinstance(arg,list) else [arg], args), [])
print(concat(a,b,c,d,e))
Outputs:
[1, 2, 3, 'de', 5, 4, 5, 23, 11, 5, 'dg', 'kuku']
You have to combine append() and extend() because one of your examples is not a list (b and c) but a single integer.
#!/usr/bin/env python3
a = [1, 2, 3]
b = "de"
c = 5
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
the_input = [a, b, c, d, e]
result = []
for element in the_input:
if isinstance(element, list):
result.extend(element)
else:
result.append(element)
print(result)
I am not aware of any chain like method to improve that example.
I wouldn't recommend this, but here's another way of doing it if you like list comprehensions or one-liners:
to_concat = [a, b, c]
concatenated_list = []
concatenated_list += [item for sublist in [[list_or_val] if not isinstance(list_or_val, list) else list_or_val for list_or_val in to_concat] for item in sublist]
Output with a, b, c = "de", [1, 2], ["dg", "kuku"] :
In [6]: concatenated_list
Out[6]: ['de', 1, 2, 'dg', 'kuku']
How does it work?
This part:
[[list_or_val] if not isinstance(list_or_val, list) else list_or_val for list_or_val in to_concat]
of the list comprehension creates a new list by transforming non-list values (like a in our case) into lists (so "de" becomes ["de"]). Let's call it list_of_lists. We now want to flatten list_of_lists to get our end result, and we can do so by the other part of the list comprehension:
[item for sublist in list_of_lists for item in sublist]
(More info here on the flattening if you're interested)
As I've said, I wouldn't recommend this solution. It's quite a mess to understand, and it probably has terrible performance, so it's not suited to larger workloads.
Great question - it lead to making something I will be putting in my utilities toolbox:
Custom generator - chainanything()
I would create the helper function as a generator rather than returning a list. This keeps it more flexible for usage and is usually a tiny bit faster. I usually do this if I have a function that returns a list.
def chainanything(*args, preservestrings=True, recursive=False):
"""
Generator: yields the contents of a Sequence, or the given object if not a Sequence, one at a time
preservestrings = False will lead to strings being yielded as individual characters. Default = True
recursive = True will recursively flatten sequences. Default = False
Note: preservestrings = False, recursive = False will only flatten strings which are not part of another Sequence.
e.g.: 'abc' -> 'a','b','c' but ['ab','cd'] -> 'ab','cd'
"""
args = [*args]
for arg in args:
if not isinstance(arg, Sequence):
yield arg
else:
if preservestrings and isinstance(arg, str):
yield arg
elif recursive:
yield from flatten(arg)
else:
yield from arg
this can then be used in it's standard form to provide your expected result:
def test_preservestring():
# https://stackoverflow.com/questions/72288401/how-to-concat-lists-integers-and-strings-into-one-string/72288721#72288721
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
assert [x for x in chainanything(a,b,c,d,e)] == [1, 2, 3, "de", 5, 4, 5, 23, 11, 5, "dg", "kuku"]
Or with join and map to answer the question in the title and concatenate them into a string:
def test_join():
a = [1, 2, 3]
b = "de" # <-- not a (usual) list !
c = 5 # <-- not a list !
d = [4, 5, 23, 11, 5]
e = ["dg", "kuku"]
assert ''.join(map(str,chainanything(a,b,c,d,e))) == "123de54523115dgkuku"
The overall function came out a little longer than one line in order to handle strings in a logical way.
The flatten function recursively flattens sequences - it's another little helper generator I created for my toolbox:
def flatten(seq):
"""
Recursively flattens a sequence (including strings!) and returns all elements in order left to right.
E.g.: [1,2,[3,4,[5],6],7,[8,9]] -> [1,2,3,4,5,6,7,8,9]
"""
for item in seq:
if not isinstance(item, Sequence):
yield item
elif len(item) == 1 and item[0] == item: #eg item = 'a'
yield item[0]
else:
yield from flatten(item)
You can grab the latest version of the helpers here: https://dev.azure.com/MusicalNinjas/_git/MikesMath
I have to make a function that takes a list and and maps two lambda functions alternatively. Like this:
>>> alternateMap(lambda x: x+1, lambda y: y+10, [1, 2, 3, 4])
[2, 12, 4, 14]
So, the first function lambda x: x+1 applies to the first element and the third element, the second function lambda y: y+10 applies to the second and fourth elements.
So far I have this:
def alternateMap(function1, function2, l):
for i in l:
a = map(function1, l)
i += 2
for n in l[1:]:
a = map(function2, l)
i += 2
return a
But unfortunately it's not correct.
This works for any number of functions.
>>> from itertools import cycle
>>> func_cycle = cycle([lambda x:x+1, lambda y:y+10])
>>> [next(func_cycle)(item) for item in [1, 2, 3, 4]]
[2, 12, 4, 14]
You stated in comments elsewhere that you need a version that uses map. Here you go:
import itertools
class callable_cycle(itertools.cycle):
def __call__(self, *args, **kwds):
return next(self)(*args, **kwds)
func_cycle = callable_cycle([lambda x:x+1, lambda y:y+10])
map(func_cycle, [1, 2, 3, 4])
def is_list(p):
return isinstance(p, list)
def deep_reverse(p):
initial = []
for v, e in enumerate(p):
if is_list(e):
#print p[v][::-1]
initial.append(p[v][::-1])
deep_reverse(e)
return initial
p = [1, [2, 3, [4, [5, 6, [7, 8]]]]]
print deep_reverse(p)
I get [[[4, [5, 6, [7, 8]]], 3, 2]], expected at least (I haven't bothered to figure out how to not lose the very first list [1[...]] yet) [[[[6, 5, [8, 7]], 4], 3, 2]].
As you can see the code only reverses [ [2, 3]] --> [[3, 2]].
What did I do wrong? Haven't I though about?
This is how I would do it:
def deep_reverse(p):
return [deep_reverse(x) if isinstance(x, list) else x for x in p[::-1]]
p = [1, [2, 3, [4, [5, 6, [7, 8]]]]]
print deep_reverse(p) # [[[[[8, 7], 6, 5], 4], 3, 2], 1]
A more generic, Pythonic answer to this, based on Pavel Anossov's is as follows:
def deep_reversed(seq):
return [deep_reversed(x) if (isinstance(x, collections.Sequence) and
not isinstance(x, str)) else x
for x in reversed(seq)]
Note that this is for Python 3.x, in Python 2.x, you will want isinstance(x, basestring) instead to allow for Unicode strings.
This answer is a good one, as it will work correctly with any object that acts as a sequence - be it a list, a tuple, or a custom class. This means it's much more flexible.
Edit: If you wanted it to reverse strings internally:
def deep_reversed(seq):
for x in reversed(seq):
if isinstance(x, collections.Sequence):
if isinstance(x, str):
yield "".join(reversed(x))
else:
yield deep_reversed(x)
else:
yield x
Again, in 2.x, use isinstance(x, basestring).
There are already many nice solutions, but maybe this is the algorithm you are trying:
def is_list(p):
return isinstance(p, list)
def deep_reverse(p):
initial = p[::-1] # reverse this level
for v, e in enumerate(initial):
if is_list(e): # for all the sublist in this level
initial[v] = deep_reverse(e) # recursively call deep_reverse to reverse the sublist
return initial
p = [1, [2, 3, [4, [5, 6, [7, 8]]]]]
print deep_reverse(p)
In the recursive calls to deep_reverse(e) you are not using the returned value. It looks as though you are expecting it to modify the input list
You can change it to something like this:
def deep_reverse(p):
initial = []
for e in p[::-1]:
if is_list(e):
initial.append(deep_reverse(e)])
else:
initial.append(e)
return initial
This will solve your purpose:
import collections
def dr(p):
r=[]
for i in p:
if isinstance(i,collections.Iterable):
r.append(dr(i))
else:
r.append(i)
return r[::-1]
I have a list with nested lists and I want to create a function (modify_list) that gets a tuple and modifies the passed pointer with a passed value argument. The problem is that I'm not sure how to modify a nested value like this programmatically by reference.
Simplified example:
l = [[1, [2,3, [4,5,6]]]]
If I call the function modify_list, these would be how to use it and the expected results:
> l[0][1][2][2]
6
> modify_list((0, 1, 2, 2), 8)
> l
[[1, [2,3, [4,5,8]]]]
> modify_list((0, 1, 1), 14)
> l
[[1, [2,14, [4,5,8]]]]
Thanks
You can determine each sublist by accessing it with the respective index. Use the last index to assign the value:
def set_nested_val(l, indices, val):
for i in indices[:-1]:
l = l[i]
l[indices[-1]] = val
Note that this function operates on an arbitrary list (the first argument), and not only l. If you want to always modify l, use functools.partial:
import functools
l = [[1, [2,3, [4,5,6]]]]
modify_list = functools.partial(set_nested_val, l)
Note that nested lists, and accessing values by indicies, are often a sign of a bad data architecture. Have you considered a dict whose keys are tuples?
def modify_list(indices, new_value):
x = reduce(lambda x, i: x[i], indices[:-1], l)
x[indices[-1]] = new_value
Example:
>>> l = [[1, [2, 3, [4, 5, 6]]]]
>>> modify_list((0, 1, 2, 2), 8)
>>> l
[[1, [2, 3, [4, 5, 8]]]]
This method matches what you are asking for in your question, but it probably makes more sense to pass in the list that you want to mutate instead of always modifying the global variable:
def modify_list(lst, indices, new_value):
x = reduce(lambda x, i: x[i], indices[:-1], lst)
x[indices[-1]] = new_value
>>> l = [[1, [2, 3, [4, 5, 6]]]]
>>> modify_list(l, (0, 1, 2, 2), 8)
>>> l
[[1, [2, 3, [4, 5, 8]]]]
Try this:
def modify_list(lst, pos, val):
item = lst
while len(pos) > 1:
item = item[pos[0]]
pos = pos[1:]
item[pos[0]] = val
Or this:
def modify_list(lst, pos, val):
item = lst
for i in pos[:-1]:
item = item[i]
item[pos[-1]] = val
Or this:
def modify_list(lst, pos, val):
reduce(lambda a, e: a[e], pos[:-1], lst)[pos[-1]] = val
In any case, use it like this:
lst = [[1, [2, 3, [4, 5, 6]]]]
modify_list(lst, (0, 1, 2, 2), 8)
lst
> [[1, [2, 3, [4, 5, 8]]]]