Skip to main content
1 of 6

Separating data from string representation of objects, with added extras

Given a string representation of data, I want to extract the information into its corresponding object.

However,

If the string has "|" separators then these should be considered options and need to be picked at random.

If the string data has numbers shown as a range "1-10" then a random value should be chosen between the range. It should also preserve the numerical datatype i.e int or float

I.e

"(1-3,1,1)" returns either (1, 1, 1), (2, 1, 1) or (3, 1, 1)

"(0.2-0.4,1,1)" returns either (0.2, 1, 1), (0.3, 1, 1) or (0.4, 1, 1)

"foo|bar|foobar" returns either "foo", "bar" or "foobar"

"(1-2,1,2)|"foo"|"bar"|(1,8-10,99)" could return :

"foo","bar", (1, 1, 2), (2, 1, 2), (1, 8, 99), (1, 9, 99) or (1, 10, 99)

This is what I have and it works well. But I cant help thing it could be achieved in a more concise way.

import re
import random
import ast

def randomize_by_pipe(st_value):
    """
    Used to split strings with the pipe character and randomly choose and option.

    :param: st_value - (str)

    """
    if not st_value is None:
        st_arr = st_value.split("|")
        random.shuffle(st_arr)
        return st_arr[0]
    else:
        return st_value

def randomise_range(text):
    if text is None:
        return text
    else:
        matches = re.findall("\d*\.*\d*-{1}\d*\.*\d*",text)
        
        for match in matches:
            startingPos = 0
            position = text.find(match, startingPos)
            while True:
                position = text.find(match, startingPos)
                if position > -1:
                    txt = text[position:position+len(match)]
                    txt = rand_no_from_string(txt)

                    new_text = text[0:position+len(match)].replace(match,str(txt))
                    text = new_text + text[position+len(match):]
                else:
                    break

        try:
            return ast.literal_eval(text)
        except ValueError:
            return text

def rand_no_from_string(txt):
    
    is_int = False
    txt_arr = txt.split("-")
    num_arr = [float(x) for x in txt_arr]
    if int(num_arr[0]) == num_arr[0]:
        mul = 1
        is_int = True
    else:
        mul = 1000
    num_arr = [x*mul for x in num_arr]
    
    if num_arr[0] > num_arr[1]:
        num_arr[1], num_arr[0] = num_arr[0], num_arr[1]
        
    val = random.randint(num_arr[0],num_arr[1])/mul
    return int(val) if is_int else val
    

Run with:

text="(108-100,0.25-0.75,100)|Foo|Bar|[123,234,234-250]"
randomise_range(randomize_by_pipe(text))