5

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?

5 Answers 5

4

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))
Sign up to request clarification or add additional context in comments.

2 Comments

I like your solution. Could be modified wit a lambda map(lambda e: e if isinstance(e, list) else [e], (a, b, c, d, e)) to "save" first 5 lines.
It could. I would advise against it, because you would have to add a comment to explain what the lambda does. Maybe the compromise lies in collapsing the function body into a single return statement: return x is isinstance(x, list) else [x].
3

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

Comments

2

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.

1 Comment

Thanks for the answer, isn't there any chain method that "knows" to append all of them to one list?
0

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.

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.