views:

330

answers:

6

I can't seem to find an elegant way to start from t and result in s.

>>>t = ['a',2,'b',3,'c',4]
#magic
>>>print s
{'a': 2, 'c': 4, 'b': 3}

Solutions I've come up with that seems less than elegant :

s = dict()
for i in xrange(0, len(t),2): s[t[i]]=t[i+1]
# or something fancy with slices that I haven't figured out yet

It's obviously easily solved, but, again, it seems like there's a better way. Is there?

+7  A: 

Not exactly efficient, but if you don't need it for very large lists:

dict(zip(t[::2], t[1::2]))

Or your version using a generator:

dict(t[i:i+2] for i in xrange(0, len(t), 2))
Lukáš Lalinský
+1  A: 
dict(zip(t[::2], t[1::2]))

probably not the most efficient. works in python 3; you might need to import zip, in python 2.x

sri
zip first appeared as a builtin in 2.0, according to the docs.
TokenMacGuy
+5  A: 

Same idea as Lukáš Lalinský's answer, different idiom:

>>> dict(zip(*([iter(t)] * 2)))
{'a': 2, 'c': 4, 'b': 3}

This uses the dict, zip and iter functions. It's advantage over Lukáš' answer is that it works for any iterable. How it works:

  1. iter(t) creates an iterator over the list t.
  2. [iter(t)] * 2 creates a list with two elements, which reference the same iterator.
  3. zip is a function which take two iterable objects and pairs their elements: the first elements together, the second elements together, etc., until one iterable is exhausted.
  4. zip(*([iter(t)] * 2)) causes the same iterator over t to be passed as both arguments to zip. zip will thus take the first and second element of t and pair them up. And then the third and fourth. And then the fifth and sixth, etc.
  5. dict takes an iterable containing (key, value) pairs and creates a dctionary out of them.
  6. dict(zip(*([iter(t)] * 2))) creates the dictionary as requested by the OP.
Stephan202
This (and all the other answers) don't seem to be particularly elegant. There's no built-in way?
Rizwan Kassim
No single function that I'm aware of, sorry.
Stephan202
+6  A: 

Guys, guys, use itertools. Your low-RAM users will thank you when the lists get large.

>>> from itertools import izip, islice
>>> t = ['a',2,'b',3,'c',4]
>>> s = dict(izip(islice(t, 0, None, 2), islice(t, 1, None, 2)))
>>> s
{'a': 2, 'c': 4, 'b': 3}

It might not look pretty, but it won't make unnecessary in-memory copies.

Steve Losh
Indeed in Python 2 one should use `itertools`. In Python 3 however, `zip` returns a generator. In that case `dict(zip(*([iter(t)] * 2)))` will do just as well.
Stephan202
Since the original question used `print s` and not `print(s)` I assumed Python 2 :)
Steve Losh
This seems more complex than would be necessary-but I appreciate the lack of in-memory copies =)
Rizwan Kassim
+5  A: 

I'd use itertools, but, if you think that's complicated (as you've hinted in a comment), then maybe:

def twobytwo(t):
  it = iter(t)
  for x in it:
    yield x, next(it)

d = dict(twobytwo(t))

or equivalently, and back to itertools again,

def twobytwo(t):
  a, b = itertools.tee(iter(t))
  next(b)
  return itertools.izip(a, b)

d = dict(twobytwo(t))

or, if you insist on being inline, in a season-appropriate "trick or treat" mood:

d = dict((x, next(it)) for it in (iter(t),) for x in it)

me, I consider this a trick, but some might find it a treat. IOW, I find this kind of thing scary, but apparently in the US around this time of the years things are supposed to be;-).

Basically, the problem boils down to "how do I walk a list 2 items at a time", because dict is quite happy to take a sequence of 2-tuples and make it into a dictionary. All the solutions I'm showing here ensure only O(1) extra space is taken (beyond the space, obviously O(N), that's needed for the input list and the output dict, of course).

The suggested approach in the docs (everybody should be familiar with that page, the itertool recipes) is the function pairwise on that page, which is basically the second one I suggested here. I do think every site-packages directory should contain an iterutils.py file with those recipes (pity such a file's not already a part of python's stdlib!-).

Alex Martelli
+1 for the "trick" and your remark about `iterutils.py`.
Stephan202
+1  A: 

Using the stream module:

>>> from stream import chop
>>> t = ['a',2,'b',3,'c',4]
>>> s = t >> chop(2) >> dict
>>> s
{'a': 2, 'c': 4, 'b': 3}
intuited