Forum Archive

True / False Variables in python

Seb

Hi, I am pretty new to coding in general and have been following lots of tutorials to try and learn python. I am attempting a question from one of the tutorials, translating digits to letters.

def keypad_string(keys):
    '''
    Given a string consisting of 0-9,
    find the string that is created using
    a standard phone keypad

    | 1         | 2 (abc)| 3 (def)  |
    | 4 (ghi)   | 5 (jkl)   | 6 (mno)|
    | 7 (pqrs)| 8 (tuv) | 9 (wxyz)|
    |   *   |   0 ()    |   #   |

    You can ignore 1, and 0 corresponds to space
        >>> keypad_string('12345')
    'adgj'
    >>> keypad_string('4433555555666')
    'hello'
    >>> keypad_string('2022')
    'a b'
    >>> keypad_string('')
    ''
    >>> keypad_string('111')
    ''

The way I thought of initially was to split all the letters into their corresponding groups before translating them. So a string ‘1223333’ would end up being ‘1’, ‘22’, ‘333’, ‘3’. I am aiming to check the digit in the string, see if it is the same as the previous digit, if not move the digit into the final string, and add the new digit into the buffer to correctly split the numbers into their groups.

Here is what I have so far, but I can’t seem to get the True/ False variables to change, so everything ends up in the buffer + final string!

keys = '1223333'
keylist = list(keys)
global sameletter
sameletter = True           

def splitter(keylist):
    split_letters = []
    buffer = []
    previousdigit = ''
    for digit in keylist:   
        check_previous(digit, previousdigit)    
        check_buffer_len(buffer, previousdigit)
        if sameletter == True:
            buffer.append(digit)
            previousdigit = digit
        else:
            split_letters.append(buffer)
            buffer.clear
            buffer.append(digit)
            previousdigit = digit

    print(split_letters)        

def check_previous(digit, previousdigit):
    if digit != previousdigit:
        sameletter = False

def check_buffer_len(buffer, previousdigit):
    if len(buffer) == 3:
        if previousdigit in {'7','9'}:
            sameletter = True
        else:
            sameletter = False


splitter(keys)

I think the problem, looking through it with the debugger, is that the sameletter variable never actually changes to false? Would anybody be able to help

Thanks!

mikael

@Seb, please surround your code with three back ticks (```) to make it readable.

Meanwhile, of course, there already is a function for the grouping. The following gives you a list of tuples, where the first item is the key pressed, and the second is how many times it was pressed.

import itertools

digits = [
    (key, len(list(grouper)))
    for key, grouper
    in itertools.groupby('4433555555666')
]

print(digits)
stephen

@mikael said:

@Seb, please surround your code with three back ticks (```) to make it readable.

I think it's would be like this..


keys = '1223333'
keylist = list(keys)
global sameletter
sameletter = True

def splitter(keylist):
    split_letters = []
    buffer = []
    previousdigit = ''

    for digit in keylist:
        check_previous(digit, previousdigit)
        check_buffer_len(buffer, previousdigit)

    if sameletter == True:
        buffer.append(digit)
        previousdigit = digit
    else:
        split_letters.append(buffer)
        buffer.clear
        buffer.append(digit)
        previousdigit = digit

print(split_letters) 

def check_previous(digit, previousdigit):
    if digit != previousdigit:
        sameletter = False

def check_buffer_len(buffer, previousdigit):
    if len(buffer) == 3:
        if previousdigit in {'7','9'}:
            sameletter = True
        else:
            sameletter = False

splitter(keys)

but i get

NameError: name 'split_letters' is not defined

Seb

@mikael said:

@Seb, please surround your code with three back ticks (```) to make it readable.

Meanwhile, of course, there already is a function for the grouping. The following gives you a list of tuples, where the first item is the key pressed, and the second is how many times it was pressed.

```
import itertools

digits = [
(key, len(list(grouper)))
for key, grouper
in itertools.groupby('4433555555666')
]

print(digits)
```

Thanks! now edited.

groupby is interesting, thanks for that suggestion. My original reasoning for splitting the digits up though, is if the string has ‘333333’ it would end up being ‘333’, ‘333’ and translate to ‘f’,’f’ eventually. However if it was ‘777777’ you can press 7 four times not three, so the grouping would end up ‘7777’, ‘77’.

I am trying to find an elegant solution to splitting the numbers either when they change or when they’ve reached the repeat limit (and cycle to the next letter)

If i run the groupby solution I would end up with (‘3’, 6), (‘7’, 6) which could be useful but would then still need splitting again, if you see what I mean?

Seb

@stephen said:

but i get

NameError: name 'split_letters' is not defined

Thanks, have edited it now. Do you still get ‘split_letters’ is not defined? The code runs for me but I just get a blank output ‘[ ]’

pulbrich

Move the line

print(split_letters) 

to the right, so that it aligns with the 'else' above it. Currently it is out of the scope of the splitter(), which defines it.

7upser

Some thoughts from my Side:

You can iterate your Str keys, no need for keylist.
In your Function you create a local variable sameletter. You dont change your global Variable.

Add: global sameletter
at the start of your functions
or work with return

And i dont like the Idea that there are two functions, called directly one after one, and both change the same variable. I think one function is better.

mikael

@Seb, others have given you good advice on progressing with your code. Especially good to understand that global is not something you declare at the top of the file, but something you use within a function to ”pull” a global variable into the function scope.

See below how I would complete the groupby approach. Note the convenience of a defined ”keypad” where I later added some Nordic letters without changing any code. Also please note the ”effect” test case I added to demonstrate the use of ”1” as a disambiguator.

(You can run the doctests by long-pressing on the play button in Pythonista.)


import itertools


keypad = {
    '1': '', '2': 'abcä', '3': 'def',
    '4': 'ghi', '5': 'jkl', '6': 'mnoö',
    '7': 'pqrs', '8': 'tuv', '9': 'wxyz',
    '0': ' ',
}


def keypad_string(as_entered):
    """
        >>> keypad_string('12345')
        'adgj'
        >>> keypad_string('4433555555666')
        'hello'
        >>> keypad_string('2022')
        'a b'
        >>> keypad_string('')
        ''
        >>> keypad_string('111')
        ''
        >>> keypad_string('331333333332228')
        'effect'
        >>> keypad_string('21222266616666')
        'aäoö'
    """        
    groups = [
        [digit, len(list(grouper))]
        for digit, grouper in itertools.groupby(as_entered)
        if digit != '1'
    ]


    result = ''
    for digit, count in groups:
        letters = keypad[digit]
        result += letters[-1] * (count // len(letters))
        result += letters[:count % len(letters)][-1:]

    return result
Seb

@mikael
@7upser

Thank you both for your help. I am going to do some lessons on using return in functions as I’ve realised I don’t fully understand it.

7upser, I didn’t realise you could iterate without it being in a list so have removed that, and also simplified it so there isn’t two functions directly after each other changing the same variable. Is that something thats just ‘best practise?’ (Although just a fledgling hobby at the moment I don’t want to pick up bad habits)

Mikael that is a frustratingly short solution! I have a lot to learn. Thanks for your help.

Here is the code now it works :) I just need to convert to letters which i’ll look at shortly.

````
keys = '12233333333337777777777'
sameletter = True

def splitter(keys):
global sameletter
split_letters = []
buffer = []
previousdigit = ''
for digit in keys:
if digit in {previousdigit, "''"}:
check_buffer_len(buffer, previousdigit)
else:
sameletter = False

    if sameletter == True:
        buffer.append(digit)
        previousdigit = digit
    else:
        split_letters.append(buffer)
        buffer = []
        buffer.append(digit)
        previousdigit = digit

split_letters.append(buffer)

print(split_letters)

def check_buffer_len(buffer, previousdigit):
global sameletter
if len(buffer) < 3:
sameletter = True
return
if len(buffer) < 4 and previousdigit in {'7','9'}:
sameletter = True
else:
sameletter = False

mikael

@Seb, thanks.

If we really focused on lines of code instead of readability, we could have:


def keypad_string(as_entered):
    return ''.join((
        letters[-1] * (count // len(letters)) + 
        letters[:count % len(letters)][-1:]
        for digit, count, letters
        in (
            (digit, len(list(grouper)), keypad[digit])
            for digit, grouper
            in itertools.groupby(as_entered)
            if digit != '1'
        )
    ))
7upser

@mikael: jesus and wtf and a lot more... :)

@Seb,
I come from Basic (ZX81) and be sure i know nothing about best practice. I have only some experience.
Changing the same variable within 2 defs at the same section of code is a good error source.

And for Return, its easy you just return a value (or more).

def function1():
    variable1 = 'Hallo World'
    return variable1

def function2(str1):
    return 'Hello ' + str1


print(function1())
print(function2('World')) 
lpoloyas

@Seb said:

The way I thought of initially was to split all the letters into their corresponding groups before translating them. So a string ‘1223333’ would end up being ‘1’, ‘22’, ‘333’, ‘3’. I am aiming to check the digit in the string, see if it is the same as the previous digit, if not move the digit into the final string, and add the new digit into the buffer to correctly split the numbers into their groups.

The way I thought of initially was to split all the letters into their corresponding groups before translating them. So a string ‘1223333’ would end up being ‘1’, ‘22’, ‘333’, ‘3’. I am aiming to check the digit in the string, see if it is the same as the previous digit, if not move the digit into the final string, and add the new digit into the buffer to correctly split the numbers into their groups.