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
Related
I would like to append to a new list all elements of an existing list of lists after a specific point
m = [[1,2,3],[4,5,10],[6,2,1]]
specific point = m[0][2]
newlist = [3,4,5,10,6,2,1]
You can directly slice off the remainder of the first target list and then add on all subsequent elements, eg:
m = [[1,2,3],[4,5,10],[6,2,1]]
y, x = 0, 2
new_list = m[y][x:] + [v for el in m[y+1:] for v in el]
# [3, 4, 5, 10, 6, 2, 1]
Here's a couple of functional approaches for efficiently iterating over your data.
If sublists are evenly sized, and you know the index from where to begin extracting elements, use chain + islice:
from itertools import chain, islice
n = 3 # Sublist size.
i,j = 0,2
newlist = list(islice(chain.from_iterable(m), i*n + j, None))
If you don't know the size of your sublists in advance, you can use next to discard the first portion of your data.
V = chain.from_iterable(m)
next(v for v in V if v == m[i][j])
newlist = list(V)
newlist.insert(m[i][j], 0)
This assumes there is no identical value earlier in the sequence.
You can put a conditional in your iteration and only add based on that condition. Once you hit that specific index, make your condition true. Something like this:
m = [[1,2,3],[4,5,10],[6,2,1]]
specific_point = (0,2)
newlist = [3,4,5,10,6,2,1]
output = []
for i in range(len(m)):
for j in range(len(m[i])):
if (i,j) < specific_point:
continue
output.append(m[i][j])
output:
[3, 4, 5, 10, 6, 2, 1]
why not flatten the initial list and go from there
flat_list = [item for sublist in m for item in sublist]
would return [1,2,3,4,5,10,6,2,1] so now you're really on flat_list[2:]
Most of the answers only work for this specific shape of nested list, but it's also possible to create a solution that works with any shape of nested list.
def flatten_from(sequence, path=[]):
start = path.pop(0) if path else 0
for item in sequence[start:]:
if isinstance(item, (list, tuple)):
yield from flatten_from(item, path)
else:
yield item
With the example from the question
>>> list(flatten_from([[1, 2, 3], [4, 5, 10], [6, 2, 1]], [0, 2]))
[3, 4, 5, 10, 6, 2, 1]
It also works with any shape and level of nesting of the input data
m = [[1], [[2], [3, 4, 5, 6, 7]], 8, [9, [10, 11]]]
flatten_from(m, [])) # 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
flatten_from(m, [2]) # 8, 9, 10, 11
flatten_from(m, [1, 1, 3]) # 6, 7, 8, 9, 10, 11
This is a bit of a bastard algorithm, though. On one hand, it uses nice functional programming concepts: recursion and yield.
On the other hand it relies on the side effect of mutating the path argument with list.pop, so it's not a pure function.
Below solution will work for your case where your array is restricted to list of list and the size of 'sublist' is consistent throughout i.e "3" in your case
m = [[1,2,3],[4,5,10],[6,2,1]] #input 2D array
a, b = 0, 2 #user input --> specific point a and b
flat_list_m = [item for firstlist in m for item in firstlist] #flat the 2D list
print (flat_list_m[len(m[0])*a+b:]) #print from specific position a and b, considering your sublist length is consistent throughout.
I hope this helps! :)
Given an array a = [1,2,3,[4,5]] using Python 3, how can I add all the elements in the array?
sum(a[0])
sun(a[0][1])
The above code did not work. Also in the case, if you're given an array with an unknown amount of arrays inside arrays, How can those numbers be calculated?
def xsum(x):
if not x:
return 0
head, *tail = x
if isinstance(head, list):
return xsum(head) + xsum(tail)
elif tail:
return head + xsum(tail)
else:
return head
You need a flatten function. Here is one:
def flatten(a):
"""generator of flattened n-deep iterable (excluding str) a."""
for elem in a:
if not isinstance(elem, str):
try:
yield from flatten(elem)
except TypeError:
yield elem
else:
yield elem
which you can then use in sum, for example:
a = [1, 2, 3, [4, [5, 6]]
print(list(flatten(a))) # --> [1, 2, 3, 4, 5, 6]
print(sum(flatten(a))) # --> 21
You can use functools.reduce to sum this nested list
>>> from functools import reduce
>>> a = [1,2,3,[4,5]]
>>> reduce(lambda x,y: x + (sum(y) if type(y)==list else y), [0]+a)
15
If the list can be more than one level nested, you have to use a recursive approach
>>> f = lambda x,y: x + (reduce(f, y) if type(y)==list else y)
>>> reduce(f, [0]+a)
15
You can use the closure property for finding sum of infinite nested list.
def nested_list_sum(x):
c = []
def l_sum(x):
for i in x:
if isinstance(i, list):
l_sum(i)
else:
c.append(i)
l_sum(x)
return sum(c)
like
a = [1,2,3,[4,5]] ----> 15
a = [1,2,3,[4,5, [6,7, [8, 9]]], [10, 11, 12, [13, 14, 5]]] -- > 110
The aa list is my input, and the bb list my desired output. The operation is rather simple (change every element in aa to string appending some characters, while keeping the original shape), but the way I do it seems unnecessarily convoluted.
Is there a better (more Pythonic) way to do this?
aa = [0, 5, 6, [8], 3, [9, 2]]
bb = []
for e1 in aa:
if type(e1) == list:
bb2 = []
for e2 in e1:
bb2.append('sss' + str(e2))
bb.append(bb2)
else:
bb.append('sss' + str(e1))
print(bb)
['sss0', 'sss5', 'sss6', ['sss8'], 'sss3', ['sss9', 'sss2']]
You can use a recursive list comprehension to achieve this
def f(data):
return [f(i) if isinstance(i, list) else 'sss{}'.format(i) for i in data]
For example
>>> aa = [0, 5, 6, [8], 3, [9, 2]]
>>> f(aa)
['sss0', 'sss5', 'sss6', ['sss8'], 'sss3', ['sss9', 'sss2']]
You could do what your question title is already hinting at, i.e. use recursion:
def transform(ll):
if isinstance(ll, list):
return list(map(transform, ll))
else:
return 'sss%s' % ll
print(transform([0, 5, 6, [8], 3, [9, 2]]))
Well, one way to do this recursively that is a little more concise is
def addSSS(item):
if isinstance(item, list):
# map takes a function, and a list and returns a
# new list that is the result of applying the function
# to each element. list() converts the result to a list
return list(map(addSSS, item))
# if it's not a list simply append the string and return that value
return "sss" + str(item)
bb = list(map(addSSS, aa))
Maps are nice because they don't evaluate each element right away, only on demand, of course here it gets evaluated right away when you call list() to turn it back to a list
This has the additional advantage that it works when your list nests deeper like aa = [1, 2, [1, 2, [1, 2]]]
I want to do the following in Python:
A = [1, 2, 3, 4, 5, 6, 7, 7, 7]
C = A - [3, 4] # Should be [1, 2, 5, 6, 7, 7, 7]
C = A - [4, 3] # Should not be removing anything, because sequence 4, 3 is not found
So, I simply want to remove the first appearance of a sublist (as a sequence) from another list. How can I do that?
Edit: I am talking about lists, not sets. Which implies that ordering (sequence) of items matter (both in A and B), as well as duplicates.
Use sets:
C = list(set(A) - set(B))
In case you want to mantain duplicates and/or oder:
filter_set = set(B)
C = [x for x in A if x not in filter_set]
If you want to remove exact sequences, here is one way:
Find the bad indices by checking to see if the sublist matches the desired sequence:
bad_ind = [range(i,i+len(B)) for i,x in enumerate(A) if A[i:i+len(B)] == B]
print(bad_ind)
#[[2, 3]]
Since this returns a list of lists, flatten it and turn it into a set:
bad_ind_set = set([item for sublist in bad_ind for item in sublist])
print(bad_ind_set)
#set([2, 3])
Now use this set to filter your original list, by index:
C = [x for i,x in enumerate(A) if i not in bad_ind_set]
print(C)
#[1, 2, 5, 6, 7, 7, 7]
The above bad_ind_set will remove all matches of the sequence. If you only want to remove the first match, it's even simpler. You just need the first element of bad_ind (no need to flatten the list):
bad_ind_set = set(bad_ind[0])
Update: Here is a way to find and remove the first matching sub-sequence using a short circuiting for loop. This will be faster because it will break out once the first match is found.
start_ind = None
for i in range(len(A)):
if A[i:i+len(B)] == B:
start_ind = i
break
C = [x for i, x in enumerate(A)
if start_ind is None or not(start_ind <= i < (start_ind + len(B)))]
print(C)
#[1, 2, 5, 6, 7, 7, 7]
I considered this question was like one substring search, so KMP, BM etc sub-string search algorithm could be applied at here. Even you'd like support multiple patterns, there are some multiple pattern algorithms like Aho-Corasick, Wu-Manber etc.
Below is KMP algorithm implemented by Python which is from GitHub Gist.
PS: the author is not me. I just want to share my idea.
class KMP:
def partial(self, pattern):
""" Calculate partial match table: String -> [Int]"""
ret = [0]
for i in range(1, len(pattern)):
j = ret[i - 1]
while j > 0 and pattern[j] != pattern[i]:
j = ret[j - 1]
ret.append(j + 1 if pattern[j] == pattern[i] else j)
return ret
def search(self, T, P):
"""
KMP search main algorithm: String -> String -> [Int]
Return all the matching position of pattern string P in S
"""
partial, ret, j = self.partial(P), [], 0
for i in range(len(T)):
while j > 0 and T[i] != P[j]:
j = partial[j - 1]
if T[i] == P[j]: j += 1
if j == len(P):
ret.append(i - (j - 1))
j = 0
return ret
Then use it to calcuate out the matched position, finally remove the match:
A = [1, 2, 3, 4, 5, 6, 7, 7, 7, 3, 4]
B = [3, 4]
result = KMP().search(A, B)
print(result)
#assuming at least one match is found
print(A[:result[0]:] + A[result[0]+len(B):])
Output:
[2, 9]
[1, 2, 5, 6, 7, 7, 7, 3, 4]
[Finished in 0.201s]
PS: You can try other algorithms also. And #Pault 's answers is good enough unless you care about the performance a lot.
Here is another approach:
# Returns that starting and ending point (index) of the sublist, if it exists, otherwise 'None'.
def findSublist(subList, inList):
subListLength = len(subList)
for i in range(len(inList)-subListLength):
if subList == inList[i:i+subListLength]:
return (i, i+subListLength)
return None
# Removes the sublist, if it exists and returns a new list, otherwise returns the old list.
def removeSublistFromList(subList, inList):
indices = findSublist(subList, inList)
if not indices is None:
return inList[0:indices[0]] + inList[indices[1]:]
else:
return inList
A = [1, 2, 3, 4, 5, 6, 7, 7, 7]
s1 = [3,4]
B = removeSublistFromList(s1, A)
print(B)
s2 = [4,3]
C = removeSublistFromList(s2, A)
print(C)
How can I create a function that can take any list and extend it. Here is what I am talkig about:
def list(l, p):
return l.extend(p)
I also want to have p to be of any nature.
Please don't override the keywords, here I post an answer based on my understanding of your question, which is not very clear.
def my_list(l, *args):
from itertools import chain
sub_lists = filter(lambda x: isinstance(x, list), args)
elements = filter(lambda x: not isinstance(x, list), args)
l.extend(list(chain.from_iterable(sub_lists)))
l.extend(list(elements))
return l
l1 = [1, 2]
l2 = [3, 4]
l3 = [5, 6]
e = 7
new_list = my_list(l1, l2, l3, e, 8)
# [1, 2, 3, 4, 5, 6, 7, 8]
print(new_list)