85

I know that a list can be joined to make one long string as in:

x = ['a', 'b', 'c', 'd']
print ''.join(x)

Obviously this would output:

'abcd'

However, what I am trying to do is simply join the first and second strings in the list, then join the third and fourth and so on. In short, from the above example instead achieve an output of:

['ab', 'cd']

Is there any simple way to do this? I should probably also mention that the lengths of the strings in the list will be unpredictable, as will the number of strings within the list, though the number of strings will always be even. So the original list could just as well be:

['abcd', 'e', 'fg', 'hijklmn', 'opq', 'r'] 
philipxy
  • 14,867
  • 6
  • 39
  • 83
John
  • 853
  • 1
  • 7
  • 4
  • *“I should probably also mention that the lengths of the strings in the list will be unpredictable”* – So does the length matter? I.e. do you just want to join every pair of list elements, or do you actually want to look at the content and join as long as the resulting element stays below some special length limit? – poke May 01 '11 at 20:08
  • simply join every pair, i just thought that having not knowing the number of pairs could be a problem – John May 01 '11 at 20:14
  • Iterate over the list in pairs, and join each pair. See the linked duplicate. – Karl Knechtel Jun 25 '22 at 02:21

7 Answers7

81

You can use slice notation with steps:

>>> x = "abcdefghijklm"
>>> x[0::2] #0. 2. 4...
'acegikm'
>>> x[1::2] #1. 3. 5 ..
'bdfhjl'
>>> [i+j for i,j in zip(x[::2], x[1::2])] # zip makes (0,1),(2,3) ...
['ab', 'cd', 'ef', 'gh', 'ij', 'kl']

Same logic applies for lists too. String lenght doesn't matter, because you're simply adding two strings together.

utdemir
  • 26,532
  • 10
  • 62
  • 81
  • 1
    There is no doubt that kevpie's answer is far better. In this one, ``x[:::2]`` creates an object, ``x[1::2]`` creates another object, these creations being probably based on calculation of indexes under the hood, and a call to a function with these two objects passed as arguments is necessary before being able to obtain the successive pairs of elements that must be concatenated. While in kevpie's answer, there's just the creation of one iterator and then the iteration skips from element to element of the untouched list without having to take care of the indexes, and that's far more pythonic. – eyquem Apr 07 '14 at 12:47
  • @eyquem, using `itertools.islice` instead of [], eliminates the intermediate objects. But since both answers works on same conditions and returns same, they are both right. And `zip(i[::2], i[1::2])` looks so sweet to me, so, why not? :) – utdemir Jun 09 '14 at 15:08
  • This only works on [sequences](https://docs.python.org/3/glossary.html#term-sequence), while @kevpie's answer is more general and works on any [iterable](https://docs.python.org/3/glossary.html#term-iterable). – Kos Mar 26 '18 at 15:05
41

Use an iterator.

List comprehension:

>>> si = iter(['abcd', 'e', 'fg', 'hijklmn', 'opq', 'r'])
>>> [c+next(si, '') for c in si]
['abcde', 'fghijklmn', 'opqr']
  • Very efficient for memory usage.
  • Exactly one traversal of s

Generator expression:

>>> si = iter(['abcd', 'e', 'fg', 'hijklmn', 'opq', 'r'])
>>> pair_iter = (c+next(si, '') for c in si)
>>> pair_iter # can be used in a for loop
<generator object at 0x4ccaa8>
>>> list(pair_iter) 
['abcde', 'fghijklmn', 'opqr']
  • use as an iterator

Using map, str.__add__, iter

>>> si = iter(['abcd', 'e', 'fg', 'hijklmn', 'opq', 'r'])
>>> map(str.__add__, si, si)
['abcde', 'fghijklmn', 'opqr']

next(iterator[, default]) is available starting in Python 2.6

kevpie
  • 25,206
  • 2
  • 24
  • 28
5

just to be pythonic :-)

>>> x = ['a1sd','23df','aaa','ccc','rrrr', 'ssss', 'e', '']
>>> [x[i] + x[i+1] for i in range(0,len(x),2)]
['a1sd23df', 'aaaccc', 'rrrrssss', 'e']

in case the you want to be alarmed if the list length is odd you can try:

[x[i] + x[i+1] if not len(x) %2 else 'odd index' for i in range(0,len(x),2)]

Best of Luck

mahmoh
  • 802
  • 1
  • 9
  • 15
2

Without building temporary lists:

>>> import itertools
>>> s = 'abcdefgh'
>>> si = iter(s)
>>> [''.join(each) for each in itertools.izip(si, si)]
['ab', 'cd', 'ef', 'gh']

or:

>>> import itertools
>>> s = 'abcdefgh'
>>> si = iter(s)
>>> map(''.join, itertools.izip(si, si))
['ab', 'cd', 'ef', 'gh']
pillmuncher
  • 10,094
  • 2
  • 35
  • 33
  • Nice, but considering my code leaves me starting with the original list anyway, i think ill opt for utdmr's....thank you though – John May 01 '11 at 20:24
1

Well I would do it this way as I am no good with Regs..

CODE

t = '1. eat, food\n\
7am\n\
2. brush, teeth\n\
8am\n\
3. crack, eggs\n\
1pm'.splitlines()

print [i+j for i,j in zip(t[::2],t[1::2])]

output:

['1. eat, food   7am', '2. brush, teeth   8am', '3. crack, eggs   1pm']  

Hope this helps :)

Sravan K Ghantasala
  • 1,058
  • 8
  • 14
1
>>> lst =  ['abcd', 'e', 'fg', 'hijklmn', 'opq', 'r'] 
>>> print [lst[2*i]+lst[2*i+1] for i in range(len(lst)/2)]
['abcde', 'fghijklmn', 'opqr']
1

I came across this page interesting yesterday while wanting to solve a similar issue. I wanted to join items first in pairs using one string in between and then together using another string. Based on the code above I came up with the following function:

def pairs(params,pair_str, join_str):
"""Complex string join where items are first joined in pairs
"""
terms = iter(params)
pairs = [pair_str.join(filter(len, [term, next(terms, '')])) for term in terms]
return join_str.join(pairs)

This results in the following:

a = ['1','2','3','4','5','6','7','8','9']
print(pairs(a, ' plus ', ' and '))
>>1 plus 2 and 3 plus 4 and 5 plus 6 and 7 plus 8 and 9

The filter step prevents the '' which is produced in case of an odd number of terms from putting a final pair_str at the end.

Stephen Ellwood
  • 394
  • 1
  • 2
  • 11