pythonreplaceunicodespace

Replace space with a random character (there and back) in Python


The problem

I have a haystack:

source = '''    "El Niño" "Hi there! How was class?"

    "Me" "Good..."

    "I can't bring myself to admit that it all went in one ear and out the other."
    
    "But the part of the lesson about writing your own résumé was really interesting!"

    "Me" "Are you going home now? Wanna walk back with me?"

    "El Niño" "Sure!"'''

I have a mask:

out_table = '→☺☻♥♦♣♠•◘○§¶▬↨↑↓←∟↔'

And I have a token -- (single space).

All their elements are strings (class of <str>).

I need a function that will:

  1. Iterate through haystack (source)
  2. Replace each occurrence of token ( ) with a single character randomly picked from the mask
  3. Will print resulting new haystack after the replacement process

Finally, I need a similar method that will revert the above process, so it will replace each occurrence (every character) of the →☺☻♥♦♣♠•◘○§¶▬↨↑↓←∟↔ list into (space).

Expected result

An example (can vary -- randomness) example result is (just a printout):

↔◘↔▬"El→Niño"↓"Hi∟there!↓How↨was↨class?"

↔◘↔▬"Me"↓"Good..."

♥♦♣♠"I↓can't↨bring§myself↓to∟admit↓that↓it↓all↓went↓in↓one§ear↓and↓out§the↓other."

↔◘↔▬"But☻the☻part☻of↓the→lesson∟about↓writing↓own↓résumé§was§really→interesting!"

↔◘↔▬"Me"↓"Are↓you☻going↓home§now?→Wanna↓walk∟back↓with↓me?"

♥♦♣♠"El↓Niño"→"Sure!"

Assumptions:

So, I the most "randomly border" scenario all spaces will be replaced with the same character. Which isn't a problem at all as long as the whole process is reversible back to the original haystack (source).

My research and solution attempt

Since this is my first Python code, I have browsed a number of Python and non-Python related questions here and in the net and I have come with the following idea:

import random 
def swap_charcter(message, character, mask):
    the_list = list(message)
    for i in random.sample(range(len(the_list)), len(list(mask))):
        the_list[i] = random.choice(mask)
    return message.join(the_list)
    
# print(swap_charcter('tested', 'e', '!#'))
print(swap_charcter('tested', 'e','→☺☻♥♦♣♠•◘○§¶▬↨↑↓←∟↔'))

But... I must be doing something wrong, because each time I run this (or many, many other) piece of code with just a space as an argument, I am getting the Sample larger than population or is negative error.

Can someone help here a little bit? Thank you.

EDIT: I have replaced list(character)list(message), as suggested in the comments.


Solution

  • A quick example:

    import random 
    
    mask = '→☺☻♥♦♣♠•◘○§¶▬↨↑↓←∟↔'
    original = 'The quick fox jumps over the lazy dog'
    
    pieces = original.split(' ')
    filled = [piece+random.choice(mask) for piece in pieces]
    result = ''.join(filled)
    result = result[0:-1]
    
    print(result)
    

    Result:
    The♥quick○fox∟jumps•over↨the∟lazy♠dog

    This method takes your string and:

    1. breaks it apart into all the slices (pieces) that don't have spaces (split),
    2. uses list comprehension to add on a random character to the end of each piece,
    3. joins them all back together (join),
    4. and finally takes off the one extra mask character at the end by slicing the resultant string (result[0:-1])

    This solution uses the fact that strings are themselves iterables:

    from collections.abc import Iterable
    isinstance('hello', Iterable)
    

    Result: True

    You don't need to convert strings to lists to pick out individual characters or to get their length; you can do this directly.

    Putting that together in a function and letting you specify the character to replace as a bonus, you get:

    import random
    
    def swap_characters(string, character, mask):
        '''Replace all instances of a character from a string with a random sample from mask
    
        Parameters
        ----------
        string : str
            The string to be modified
        character : str
            The character to be replaced
        mask : str
            The string of characters from which to sample
    
        Returns
        -------
        str
            The modified string
        '''
    
        pieces = string.split(character)
        filled = [piece+random.choice(mask) for piece in pieces]
        result = ''.join(filled)
        return result[0:-1]
    
    mask = '→☺☻♥♦♣♠•◘○§¶▬↨↑↓←∟↔'
    example = "Let's take extra spaces. Here's 10:          "
    x = swap_characters(example, ' ', mask)
    print(x)
    

    Result:
    Let's←take¶extra☻spaces.♣Here's↨10:↑↓∟◘§☻↓☻↓◘

    The reverse is a more common scenario with a variety of options. For example:

    x = swap_characters(example, ' ', mask)
    print(x)
    for char in mask:
        x = x.replace(char, ' ')
    print(x)
    

    Result:
    Let's↓take←extra¶spaces.☺Here's☺10:♠§◘☻☺▬☺→¶←
    Let's take extra spaces. Here's 10:

    Alternatively using regular expressions:

    import re
    x = swap_characters(example, ' ', mask)
    print(x)
    set = '|'.join(mask)
    x = re.sub(set, ' ', x)
    print(x)
    

    Result:
    Let's•take¶extra↔spaces.○Here's↓10:♦↔∟♦◘♥••♣→
    Let's take extra spaces. Here's 10: