got it--thanks
got it--thanks

Reputation: 233

Convert string to list of Fractions

I'm trying to convert strings of the form

'[[2, 3.1], [4/5, 5/6]]'

to a 2d list of Fraction objects from the fractions module. In the particular case given above, the result should be

[[Fraction(2, 1), Fraction(31, 10)], [Fraction(4, 5), Fraction(5, 6)]]

The spacing in the string won't necessarily be as uniform as I've written it (no spaces, extra spaces, uneven numbers of spaces, etc).

I can do this in several steps by peeling off the outer brackets, splitting the string at the ']'s, removing empty strings from the resulting list, removing any nondigit from the beginning of each string in the list in a while loop, splitting each of those strings at the comma, and finally converting the elements of the nested lists to Fractions.

But that's fairly cumbersome. Is there a better solution for this?

Upvotes: 3

Views: 2515

Answers (3)

Hotschke
Hotschke

Reputation: 10190

import ast
import re
from fractions import Fraction

input_text = '[[2, 3.1], [1e100, -5/6]]'

transformed_text = ast.literal_eval(re.sub(r'([^,\[\]\s]+)', r'"\1"', input_text))
transformed_text =  [[ Fraction(j) for j in i]  for i in transformed_text ]

Upvotes: 0

Blckknght
Blckknght

Reputation: 104712

You can use regular expressions to transform the numeric literals in your string into calls to Fraction. After the transformation, you can call eval on the result to get the appropriately nested list.

You don't need to handle the division operations in a special way, since they will work exactly as you want if we just convert both numerator and denominator into Fraction objects (so 4/5 becomes Fraction("4")/Fraction("5"), which evaluates to Fraction(4, 5)).

The transformation should put quotation marks around the numbers you pass to Fraction, as that prevents eval from parsing numbers with decimal points into float instances (which usually causes a loss of precision). Fraction can do the parsing instead, from a string, and it won't lose any precision since all finite-length decimal numbers have an exact Fraction representation.

Code:

import re
from fractions import Fraction

input_text = '[[2, 3.1], [4/5, 5/6]]'

transformed_text = re.sub(r'([\d.]+)', r'Fraction("\1")', input_text)

results = eval(transformed_text)
print(results) # [[Fraction(2, 1), Fraction(31, 10)], [Fraction(4, 5), Fraction(5, 6)]]

Upvotes: 3

iam.Carrot
iam.Carrot

Reputation: 5286

I came up with a nifty code snippet that can help you out with it:

from fractions import Fraction
input_string = '[[2, 3.1], [4/5, 5/6]]'
parsed_input = [item for item in ''.join((char if char in '0123456789./' else '-') for char in input_string).split('-') if item]
output_array = [Fraction(i).limit_denominator(10) for i in parsed_input]

I hope this helps. Please use the comments section incase of any ambiguities.

Upvotes: 1

Related Questions