CodeWars - several Python challenges

Some challenges I solved on CodeWars sorted by newer to older.

Visit my profile here: https://www.codewars.com/users/r1p


6 Kyu - Detect Pangram

A pangram is a sentence that contains every single letter of the alphabet at least once. For example, the sentence "The quick brown fox jumps over the lazy dog" is a pangram, because it uses the letters A-Z at least once (case is irrelevant).

Given a string, detect whether or not it is a pangram. Return True if it is, False if not. Ignore numbers and punctuation.

Solve date: Jul 21, 2021, 1:14 PM GMT+2

Sample tests:

pangram = "The quick, brown fox jumps over the lazy dog!"
test.assert_equals(is_pangram(pangram), True)

My solution:

import string

def is_pangram(s):
    string_2 = ""
    for letter in s:
        if letter.isalpha():
            string_2 += letter

    # Convert to lower case, remove duped letters, and sort alphabetically:
    string_3 = sorted(list(dict.fromkeys(list(string_2.lower()))))

    if len(string_3) < 26:
        return False
    return True

6 Kyu - Sum of Digits / Digital Root

Digital root is the recursive sum of all the digits in a number.

Given n, take the sum of the digits of n. If that value has more than one digit, continue reducing in this way until a single-digit number is produced. The input will be a non-negative integer.

Examples

    16  -->  1 + 6 = 7
   942  -->  9 + 4 + 2 = 15  -->  1 + 5 = 6
132189  -->  1 + 3 + 2 + 1 + 8 + 9 = 24  -->  2 + 4 = 6
493193  -->  4 + 9 + 3 + 1 + 9 + 3 = 29  -->  2 + 9 = 11  -->  1 + 1 = 2

Solve date: Jul 21, 2021, 12:43 PM GMT+2

Sample tests:

test.assert_equals(digital_root(16), 7)
test.assert_equals(digital_root(942), 6)
test.assert_equals(digital_root(132189), 6)
test.assert_equals(digital_root(493193), 2)

My solution:

def digital_root(n: int) -> int:
    int_list = [int(x) for x in str(n)]

    if len(int_list) == 1:
        result = n

    while len(int_list) != 1:
        result = 0
        for x in int_list:    
            result += x
        int_list = [int(x) for x in str(result)]

    return result

5 Kyu - Simple Pig Latin

Move the first letter of each word to the end of it, then add "ay" to the end of the word. Leave punctuation marks untouched.

Examples

pig_it('Pig latin is cool') # igPay atinlay siay oolcay
pig_it('Hello world !')     # elloHay orldway !

Solve date: Jul 21, 2021, 12:28 PM GMT+2

Sample tests:

test.assert_equals(pig_it('Pig latin is cool'),'igPay atinlay siay oolcay')
test.assert_equals(pig_it('This is my string'),'hisTay siay ymay tringsay')

My solution:

def pig_it(text:str) -> str:
    result = ""
    text_list = text.split()
    for x in text_list:
        if x.isalpha():
            result += x[1:]+x[0] + "ay " # Move the first to the last position and append "ay "
        else:
            result += x + " "
    return result[:-1] # Remove the last char

8 Kyu - Vasya - Clerk

The new "Avengers" movie has just been released! There are a lot of people at the cinema box office standing in a huge line. Each of them has a single 100, 50 or 25 dollars bill. A "Avengers" ticket costs 25 dollars.

Solve date: Jul 21, 2021, 12:04 PM GMT+2

Sample tests:

test.assert_equals(tickets([25, 25, 50]), "YES")
test.assert_equals(tickets([25, 100]), "NO")

My solution:

def tickets(people:list) -> bool:
	bill_25 = 0
	bill_50 = 0
	bill_100 = 0

	for x in people:
		if x == 25:
			bill_25 += 1

		elif x == 50:
			bill_50 += 1
			bill_25 -= 1

		elif x == 100 and bill_50 > 0:
			bill_50 -= 1
			bill_25 -= 1

		elif x == 100 and bill_50 == 0:
			bill_25 -= 3

		if bill_25 < 0 or bill_50 < 0:
			return "NO"
			
	return "YES"

6 Kyu - Take a Ten Minute Walk

You live in the city of Cartesia where all roads are laid out in a perfect grid. You arrived ten minutes too early to an appointment, so you decided to take the opportunity to go for a short walk.  The city provides its citizens with a Walk Generating App on their phones -- everytime you press the button it sends you an array of one-letter strings representing directions to walk (eg. ['n', 's', 'w', 'e']).  You always walk only a single block for each letter (direction) and you know it takes you one minute to traverse one city block, so create a function that will return true if the walk the app gives you will take you exactly ten minutes (you don't want to be early or late!) and will, of course, return you to your starting point.  Return false otherwise.

Note: you will always receive a valid array containing a random assortment of direction letters ('n', 's', 'e', or 'w' only).  It will never give you an empty array (that's not a walk, that's standing still!).

Solve date: Jul 20, 2021, 8:56 PM GMT+2

Sample tests:

test.expect(is_valid_walk(['n','s','n','s','n','s','n','s','n','s']), 'should return True');
test.expect(not is_valid_walk(['w','e','w','e','w','e','w','e','w','e','w','e']), 'should return False');
test.expect(not is_valid_walk(['w']), 'should return False');
test.expect(not is_valid_walk(['n','n','n','s','n','s','n','s','n','s']), 'should return False');

My solution:

def is_valid_walk(walk: list) -> bool:
    axis_x, axis_y = 0, 0
    if len(walk) == 10: 
        for cord in walk:
            if cord == "w":
                axis_x += 1
            elif cord == "e":
                axis_x -= 1
            elif cord == "n":
                axis_y += 1
            elif cord == "s":
                axis_y -= 1
            else:
                return "Only w, e, n, s cords allowed"
        return True if axis_x == 0 and axis_y == 0 else False
    else:
        return False

6 Kyu - Replace With Alphabet Position

In this kata you are required to, given a string, replace every letter with its position in the alphabet.

If anything in the text isn't a letter, ignore it and don't return it.

"a" = 1, "b" = 2, etc.

Example

alphabet_position("The sunset sets at twelve o' clock.")

Should return "20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11" ( as a string )

Solve date: Jul 20, 2021, 8:25 PM GMT+2

Sample tests:

from random import randint
test.assert_equals(alphabet_position("The sunset sets at twelve o' clock."), "20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11")
test.assert_equals(alphabet_position("The narwhal bacons at midnight."), "20 8 5 14 1 18 23 8 1 12 2 1 3 15 14 19 1 20 13 9 4 14 9 7 8 20")

number_test = ""
for item in range(10):
    number_test += str(randint(1, 9))
test.assert_equals(alphabet_position(number_test), "")

My solution:

import string 

def alphabet_position(text):
    dict = {'a':'1','b':'2','c':'3','d':'4','e':'5','f':'6','g':'7','h':'8','i':'9','j':'10','k':'11','l':'12','m':'13','n':'14','o':'15','p':'16','q':'17','r':'18','s':'19','t':'20','u':'21','v':'22','w':'23','x':'24','y':'25','z':'26'}
    ret = ''
    for x in text.lower():
        if x in string.ascii_lowercase:
                ret += dict[x] + " "
    return ret[:-1]

8 Kyu - Format a string of names like 'Bart, Lisa & Maggie'.

Given: an array containing hashes of names

Return: a string formatted as a list of names separated by commas except for the last two names, which should be separated by an ampersand.

Example:

namelist([ {'name': 'Bart'}, {'name': 'Lisa'}, {'name': 'Maggie'} ])
# returns 'Bart, Lisa & Maggie'

namelist([ {'name': 'Bart'}, {'name': 'Lisa'} ])
# returns 'Bart & Lisa'

namelist([ {'name': 'Bart'} ])
# returns 'Bart'

namelist([])
# returns ''

Note: all the hashes are pre-validated and will only contain A-Z, a-z, '-' and '.'.

Solve date: Jul 20, 2021, 8:18 PM GMT+2

Sample tests:

test.assert_equals(namelist([{'name': 'Bart'},{'name': 'Lisa'},{'name': 'Maggie'},{'name': 'Homer'},{'name': 'Marge'}]), 'Bart, Lisa, Maggie, Homer & Marge',
"Must work with many names")
test.assert_equals(namelist([{'name': 'Bart'},{'name': 'Lisa'},{'name': 'Maggie'}]), 'Bart, Lisa & Maggie',
"Must work with many names")
test.assert_equals(namelist([{'name': 'Bart'},{'name': 'Lisa'}]), 'Bart & Lisa', 
"Must work with two names")
test.assert_equals(namelist([{'name': 'Bart'}]), 'Bart', "Wrong output for a single name")
test.assert_equals(namelist([]), '', "Must work with no names")

My solution:

def namelist(names):
    ret = ''
    if(len(names) == 1):
        return names[0]['name']
    elif(len(names) == 2):
        ret = ret + names[0]['name'] + " & " + names[1]['name']
    elif(len(names) > 2):
        for x in range(0, len(names)-1):
            ret = ret + names[x]['name'] + ", "
        ret = ret[:-2] + " & " + names[len(names)-1]['name']
    return ret

6 Kyu - Does my number look big in this?

A Narcissistic Number is a positive number which is the sum of its own digits, each raised to the power of the number of digits in a given base. In this Kata, we will restrict ourselves to decimal (base 10).

For example, take 153 (3 digits), which is narcisstic:

    1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153

and 1652 (4 digits), which isn't:

    1^4 + 6^4 + 5^4 + 2^4 = 1 + 1296 + 625 + 16 = 1938

The Challenge:

Your code must return true or false (not 'true' and 'false') depending upon whether the given number is a Narcissistic number in base 10. This may be True and False in your language, e.g. PHP.

Error checking for text strings or other invalid inputs is not required, only valid positive non-zero integers will be passed into the function.

Solve date: Jul 20, 2021, 6:21 PM GMT+2

Sample tests:

test.assert_equals(narcissistic(7), True, '7 is narcissistic');
test.assert_equals(narcissistic(371), True, '371 is narcissistic');
test.assert_equals(narcissistic(122), False, '122 is not narcissistic')
test.assert_equals(narcissistic(4887), False, '4887 is not narcissistic')

My solution:

def narcissistic(value:int) -> int:
    number_base = len(str(value))
    number_list = [int(x) for x in str(value)]
    total = 0
    for x in number_list:
        total += x**number_base

    return True if total == value else False

7 Kyu - Growth of a Population

In a small town the population is p0 = 1000 at the beginning of a year. The population regularly increases by 2 percent per year and moreover 50 new inhabitants per year come to live in the town. How many years does the town need to see its population greater or equal to p = 1200 inhabitants?

At the end of the first year there will be: 
1000 + 1000 * 0.02 + 50 => 1070 inhabitants

At the end of the 2nd year there will be: 
1070 + 1070 * 0.02 + 50 => 1141 inhabitants (** number of inhabitants is an integer **)

At the end of the 3rd year there will be:
1141 + 1141 * 0.02 + 50 => 1213

It will need 3 entire years.

More generally given parameters:

p0, percent, aug (inhabitants coming or leaving each year), p (population to surpass)

the function nb_year should return n number of entire years needed to get a population greater or equal to p.

aug is an integer, percent a positive or null floating number, p0 and p are positive integers (> 0)

Examples:
nb_year(1500, 5, 100, 5000) -> 15
nb_year(1500000, 2.5, 10000, 2000000) -> 10

Note:

Don't forget to convert the percent parameter as a percentage in the body of your function: if the parameter percent is 2 you have to convert it to 0.02.

Solve date: Jul 20, 2021, 6:14 PM GMT+2

Sample tests:

import codewars_test as test
    
@test.describe("nb_year")
def tests():
    @test.it("basic tests")
    def basics():
        test.assert_equals(nb_year(1500, 5, 100, 5000), 15)
        test.assert_equals(nb_year(1500000, 2.5, 10000, 2000000), 10)
        test.assert_equals(nb_year(1500000, 0.25, 1000, 2000000), 94)
        

My solution:

def nb_year(p0, percent, aug, p):
	i = 0
	while p0 < p:
		p0 = int(p0) + (int(p0) * percent/100) + int(aug)
		i += 1
	return i

6 Kyu - Persistent Bugger

Write a function, persistence, that takes in a positive parameter num and returns its multiplicative persistence, which is the number of times you must multiply the digits in num until you reach a single digit.

For example (Input --> Output):

39 --> 3 (because 3*9 = 27, 2*7 = 14, 1*4 = 4 and 4 has only one digit)
999 --> 4 (because 9*9*9 = 729, 7*2*9 = 126, 1*2*6 = 12, and finally 1*2 = 2)
4 --> 0 (because 4 is already a one-digit number)

Solve date: Jul 20, 2021, 5:50 PM GMT+2

Sample tests:

test.it("Basic tests")
test.assert_equals(persistence(39), 3)
test.assert_equals(persistence(4), 0)
test.assert_equals(persistence(25), 2)
test.assert_equals(persistence(999), 4)

My solution:

import sys
import numpy as np

def persistence(n: int) -> int:
	if len(str(n)) == 1:
		return 0

	result = n
	iter = 0

	while len(str(result)) != 1:
		list_n = [int(x) for x in str(result)]
		result = np.prod(list_n)
		iter += 1
	return iter


7 Kyu - Friend or Foe?

Make a program that filters a list of strings and returns a list with only your friends name in it.

If a name has exactly 4 letters in it, you can be sure that it has to be a friend of yours! Otherwise, you can be sure he's not...

Ex: Input = ["Ryan", "Kieran", "Jason", "Yous"], Output = ["Ryan", "Yous"]

i.e.

friend ["Ryan", "Kieran", "Mark"] `shouldBe` ["Ryan", "Mark"]

Solve date: Jul 20, 2021, 1:58 PM GMT+2

Sample tests:

import codewars_test as test
from solution import friend

@test.describe("Fixed Tests")
def fixed_tests():
    @test.it('Sample Test Cases')
    def sample_test_cases():
        test.assert_equals(friend(["Ryan", "Kieran", "Mark",]), ["Ryan", "Mark"])
        test.assert_equals(friend(["Ryan", "Jimmy", "123", "4", "Cool Man"]), ["Ryan"])
        test.assert_equals(friend(["Jimm", "Cari", "aret", "truehdnviegkwgvke", "sixtyiscooooool"]), ["Jimm", "Cari", "aret"])

My solution:

def friend(friends:list) -> list:
    real_friends = []

    for friend in friends:
        real_friends.append(friend) if len(friend) == 4 else 0

        '''
        Compressed if shown above equals to:
        
        if len(friend) == 4:
            real_friends.append(friend)
        '''

    return real_friends

6 Kyu - Tribonacci Sequence

Well met with Fibonacci bigger brother, AKA Tribonacci.

As the name may already reveal, it works basically like a Fibonacci, but summing the last 3 (instead of 2) numbers of the sequence to generate the next. And, worse part of it, regrettably I won't get to hear non-native Italian speakers trying to pronounce it :(

So, if we are to start our Tribonacci sequence with [1, 1, 1] as a starting input (AKA signature), we have this sequence:

[1, 1 ,1, 3, 5, 9, 17, 31, ...]

But what if we started with [0, 0, 1] as a signature? As starting with [0, 1] instead of [1, 1] basically shifts the common Fibonacci sequence by once place, you may be tempted to think that we would get the same sequence shifted by 2 places, but that is not the case and we would get:

[0, 0, 1, 1, 2, 4, 7, 13, 24, ...]

Well, you may have guessed it by now, but to be clear: you need to create a Fibonacci function that given a signature array/list, returns the first n elements - signature included of the so seeded sequence.

Signature will always contain 3 numbers; n will always be a non-negative number; if n == 0, then return an empty array (except in C return NULL) and be ready for anything else which is not clearly specified ;)

Solve date: Jul 20, 2021, 1:48 PM GMT+2

Sample tests:

test.describe("Basic tests")
test.assert_equals(tribonacci([1, 1, 1], 10), [1, 1, 1, 3, 5, 9, 17, 31, 57, 105])
test.assert_equals(tribonacci([0, 0, 1], 10), [0, 0, 1, 1, 2, 4, 7, 13, 24, 44])
test.assert_equals(tribonacci([0, 1, 1], 10), [0, 1, 1, 2, 4, 7, 13, 24, 44, 81])
test.assert_equals(tribonacci([1, 0, 0], 10), [1, 0, 0, 1, 1, 2, 4, 7, 13, 24])
test.assert_equals(tribonacci([0, 0, 0], 10), [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
test.assert_equals(tribonacci([1, 2, 3], 10), [1, 2, 3, 6, 11, 20, 37, 68, 125, 230])
test.assert_equals(tribonacci([3, 2, 1], 10), [3, 2, 1, 6, 9, 16, 31, 56, 103, 190])
test.assert_equals(tribonacci([1, 1, 1], 1), [1])
test.assert_equals(tribonacci([300, 200, 100], 0), [])
test.assert_equals(tribonacci([0.5, 0.5, 0.5], 30), [0.5, 0.5, 0.5, 1.5, 2.5, 4.5, 8.5, 15.5, 28.5, 52.5, 96.5, 177.5, 326.5, 600.5, 1104.5, 2031.5, 3736.5, 6872.5, 12640.5, 23249.5, 42762.5, 78652.5, 144664.5, 266079.5, 489396.5, 900140.5, 1655616.5, 3045153.5, 5600910.5, 10301680.5])

My solution:

def tribonacci(signature: list, n: int) -> list:
    print(f"signature: {signature}")
    print(f"n: {n}")

    if len(signature) != 3 or n <= 0:
        return []
    elif n >= 1 and n <= 3:
        return signature[:n]

    sequence = signature

    for x in range(n-3):
            new_value = signature[len(signature) -1] + signature[len(signature) -2] + signature[len(signature) -3]
            sequence.append(new_value)

    return sequence

7 Kyu - Descending Order

Your task is to make a function that can take any non-negative integer as an argument and return it with its digits in descending order. Essentially, rearrange the digits to create the highest possible number.

Examples:

Input: 42145 Output: 54421

Input: 145263 Output: 654321

Input: 123456789 Output: 987654321

Solve date: Jul 20, 2021, 12:52 PM GMT+2.

Sample tests:

import codewars_test as test

try:
    from solution import Descending_Order as descending_order
except ImportError:
    from solution import descending_order

@test.describe("Fixed Tests")
def fixed_tests():
    @test.it('Basic Test Cases')
    def basic_test_cases():
        test.assert_equals(descending_order(0), 0)
        test.assert_equals(descending_order(15), 51)
        test.assert_equals(descending_order(123456789), 987654321)

My solution:

def descending_order(num: int) -> int:
    list_num = [int(n) for n in str(num)]
    list_num.sort(reverse=True)
    list_number = [str(i) for i in list_num]
    number = "".join(list_number)
    return int(number)