42

I need to write a function that takes a string '(1,2,3,4,5),(5,4,3,2,1)' and returns a list of tuples of the 1st and last element of each tuple, [(1,5),(5,1)]. I was thinking:

def f(givenstring):
    a=givenstring.split(',')
    for i in a[0:-1]:
        tuple(int(i[0,-1]))

but here I'm stucked..

3
  • 1
    Think more like re.findall() or eval() (but remember, eval is evil). Sorry, no time for a full answer. Commented Dec 13, 2011 at 18:53
  • Can you fix the code formatting? (For some reason, I can't..) Commented Dec 13, 2011 at 18:56
  • Related: link Commented Dec 13, 2011 at 18:59

3 Answers 3

86

You can use ast.literal_eval():

Safely evaluate an expression node or a string containing a Python expression. The string or node provided may only consist of the following Python literal structures: strings, numbers, tuples, lists, dicts, booleans, and None.

This can be used for safely evaluating strings containing Python expressions from untrusted sources without the need to parse the values oneself.

In your example:

from ast import literal_eval
s = '(1,2,3,4,5),(5,4,3,2,1)'

l = literal_eval(s)
print l
# ((1, 2, 3, 4, 5), (5, 4, 3, 2, 1))

print [(x[0], x[-1]) for x in l]
# [(1, 5), (5, 1)]
Sign up to request clarification or add additional context in comments.

1 Comment

note, AST is python2.6+, I have to use eval on my python2.5 project.
33

You may use eval. I think it'll be the shortest one.

>>> s = '(1,2,3,4,5),(5,4,3,2,1)'
>>> ts = eval(s)
>>> ts
((1, 2, 3, 4, 5), (5, 4, 3, 2, 1))
>>> tsp = [(el[0],el[-1]) for el in ts]
>>> tsp
[(1, 5), (5, 1)]

Still, it's not a good practice to use eval.

Another option is to parse the string using re module.

>>> a = re.findall('\([^)]*\)',s)
>>> a
['(1,2,3,4,5)', '(5,4,3,2,1)']

Regexp pattern means this:

\( #opening parenthesis
[^)]* #from 0 to infinite symbols different from )
\) #closing parenthesis

.

>>> b = [el.strip('()') for el in a]
>>> b
['1,2,3,4,5', '5,4,3,2,1']
>>> c = [el.split(',') for el in b]
>>> c
[['1', '2', '3', '4', '5'], ['5', '4', '3', '2', '1']]
>>> d = [tuple(int(el2) for el2 in el) for el in c]
>>> d
[(1, 2, 3, 4, 5), (5, 4, 3, 2, 1)]

Also, you may do the following:

>>> [tuple(int(i) for i in el.strip('()').split(',')) for el in s.split('),(')]
[(1, 2, 3, 4, 5), (5, 4, 3, 2, 1)]

This approach takes not modules at all. But it's not very robust (if the input string will have some inconsistency, e.g. space between parentheses and comma ...), (..., then noting will work).

8 Comments

yeah, I know.. So I would like to avoid eval.. and I don't really know re.findall, so I would like to avoid that one too, if it is possible?
is there a way to combine the b,c and d lists comprehension into a single list comprehension?
@BelowtheRadar Sure [tuple(int(el2) for el2 in el.strip('()').split(',')) for el in a].
@ovgolovin could you please explain why using eval should be avoided?
@Josh Also note, there is ast.literal_eval which is safe against injections.
|
8

In this case, the ast module could be useful:

>>> from ast import literal_eval
>>> s = '(1,2,3,4,5),(5,4,3,2,1)'
>>> my_tuples = literal_eval(s)
>>> my_tuples
((1, 2, 3, 4, 5), (5, 4, 3, 2, 1))

So, my_tuples has a tuple with the tuples of your string. Now, we can get the first and last element of all your tuples using a list comprehension:

>> new_tuples = [(t[0], t[-1]) for t in my_tuples]
>>> new_tuples
[(1, 5), (5, 1)]

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.