0

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:

  • Every space must be replaced in the haystack
  • Not every character out of mask must be used

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.

trejder
  • 17,148
  • 27
  • 124
  • 216
  • 3
    `h` is larger than `n`, but you're saying to take `len(h)`-many items from `n` (or rather, a `range` the same length as `n`) with the call to `sample`. I think you have those lists backwards. – Carcigenicate May 22 '23 at 01:16
  • As I said, I am completely new to Python and I am completely loosing context here on what changes what. If you could take a look at the list line of the presented code then it should be pretty self-explanatory -- I want to _swap_ in `tested` all `e` occurrences with a random character from `→☺☻♥♦♣♠•◘○§¶▬↨↑↓←∟↔`. It would be great if last list would be used entirely, i.e. both `e`-s in source string should be replaced with a random character and result in for example: `t§st→d`. – trejder May 22 '23 at 01:23
  • 1
    `s` is the text that contains letters to be replaced, yes? Then it seems to me the first line of code should be `list(s)`, not `list(n)`... – John Gordon May 22 '23 at 02:35
  • The function never refers to `s`, except for the last line `return s.join(the_list)`, which is ... bonkers. Do you know how `join()` works? – John Gordon May 22 '23 at 02:48
  • I think it would help if you defined what each of your inputs is meant to be on your function. Maybe use names with meaning like `string` `mask` `character`. – MathCatsAnd May 22 '23 at 02:57
  • @JohnGordon Your suggestion of replacing `list(character)` into `list(message)` has been incorporated to the question and to the code, but I am still getting the same → _ValueError: Sample larger than population or is negative_. I **thought** that I know, how `join()` works in Python, but your comment has suggested to me that I am _wrong_ here! :) – trejder May 22 '23 at 07:43

2 Answers2

2

Use a re.sub (regular expression substitution) to change all spaces to a random.choice from your out_table.

To restore, use str.translate and str.maketrans to translate all characters from out_table to spaces.

Example:

import re
import random

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!"'''

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

result = re.sub(' ', lambda m: random.choice(out_table), source)
print(result)

original = result.translate(str.maketrans(out_table, ' ' * len(out_table)))
print(original)

Output:

↨◘◘↑"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!"
    "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!"
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
1

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:

MathCatsAnd
  • 601
  • 2
  • 8