In this kata, you will make a function that converts between `camelCase`, `snake_case`, and `kebab-case`.

You must write a function that changes to a given case. It must be able to handle all three case types:

```python
py> change_case("snakeCase", "snake")
"snake_case"
py> change_case("some-lisp-name", "camel")
"someLispName"
py> change_case("map_to_all", "kebab")
"map-to-all"
py> change_case("doHTMLRequest", "kebab")
"do-h-t-m-l-request"
py> change_case("invalid-inPut_bad", "kebab")
None
py> change_case("valid-input", "huh???")
None
py> change_case("", "camel")
""
```

Your function must deal with invalid input as shown, though it will only be passed strings. Furthermore, all valid identifiers will be lowercase except when necessary, in other words on word boundaries in `camelCase`.

_**(Any translations would be greatly appreciated!)**_

Difficulty: EASY
Tags: String algorithms

Starter code:
def change_case(identifier, targetCase):
	

Solutions:
import re

def change_case(label, target):
	if ('_' in label) + ('-' in label) + (label != label.lower()) > 1:
		return
	if target == 'snake':
		return re.sub('([A-Z])', '_\\1', label.replace('-', '_')).lower()
	if target == 'kebab':
		return re.sub('([A-Z])', '-\\1', label.replace('_', '-')).lower()
	if target == 'camel':
		return re.sub('([_-])([a-z])', lambda m: m.group(2).upper(), label)


def change_case(label, case):
	if case not in ['snake', 'kebab', 'camel'] or ('_' in label) + ('-' in label) + (label != label.lower()) > 1:
		return
	return ''.join(('_' + c.lower() if c.isupper() and case == 'snake' else '_' if c == '-' and case == 'snake' else '-' + c.lower() if c.isupper() and case == 'kebab' else '-' if c == '_' and case == 'kebab' else c.upper() if label[i - 1] in '-_' and case == 'camel' else '' if c in '-_' and case == 'camel' else c for (i, c) in enumerate(label)))


import re
CHANGES = {'kebab': '-', 'camel': '', 'snake': '_'}
VALID_CASE = re.compile('[A-Z_-]')
CHANGE_CASE = re.compile('[_-][a-z]|[A-Z]')

def change_case(s, targetCase):

	def convertCase(m):
		return (str.upper if targetCase == 'camel' else str.lower)(CHANGES[targetCase] + m.group()[-1])
	if targetCase in CHANGES and len({'A' if c.isalpha() else c for c in VALID_CASE.findall(s)}) <= 1:
		return CHANGE_CASE.sub(convertCase, s)


import re

def change_case(s, case):
	actions = {'snake': lambda s: re.sub('([A-Z\\-])', '_\\1', s).lower().replace('-', ''), 'camel': lambda s: re.sub('(\\-|_)(.)', lambda x: x[2].upper(), s), 'kebab': lambda s: re.sub('([A-Z_])', '-\\1', s).lower().replace('_', '')}
	return s if not s else sum((any((x.isupper() for x in s)), '_' in s, '-' in s)) < 2 and actions.get(case, lambda s: None)(s) or None


def change_case(id, t):
	q = ('_' in id) + ('-' in id) + any((x.isupper() for x in set(id)))
	if q > 1:
		return
	d = {'kebab': '-', 'snake': '_'}
	(l, index) = ([''], 0)
	for x in id:
		if not l[index] or (not x.isupper() and x.isalpha() and l[index][-1].isalpha()):
			l[index] += x
		elif x.isalpha():
			l.append(x)
			index += 1
		else:
			l.append('')
			index += 1
	if t in d:
		return f'{d[t]}'.join((x.lower() for x in l))
	if t == 'camel':
		return ''.join((w.capitalize() if i else w for (i, w) in enumerate(l)))


def change_case(identifier, targetCase):
	if isBad(identifier):
		return None
	if targetCase == 'snake':
		return snake(identifier)
	elif targetCase == 'camel':
		return camel(identifier)
	elif targetCase == 'kebab':
		return kebab(identifier)
	return None

def snake(myString):
	newString = myString
	for i in range(0, len(myString)):
		if ord(myString[i]) >= ord('A') and ord(myString[i]) <= ord('Z'):
			pos = newString.index(myString[i:])
			newString = newString[:pos] + '_' + newString[pos].lower() + newString[pos + 1:]
		elif myString[i] == '-':
			pos = newString.index(myString[i:])
			newString = newString[:pos] + '_' + newString[pos + 1:]
	return newString.lower()

def kebab(myString):
	newString = myString
	for i in range(0, len(myString)):
		if ord(myString[i]) > ord('A') - 1 and ord(myString[i]) < ord('Z') + 1:
			pos = newString.index(myString[i:])
			newString = newString[:pos] + '-' + newString[pos].lower() + newString[pos + 1:]
		if myString[i] == '_':
			pos = newString.index(myString[i:])
			newString = newString[:pos] + '-' + newString[pos + 1:]
	return newString.lower()

def camel(myString):
	if len(myString) == 0:
		return myString
	myString = myString.replace('-', '_')
	newString = myString.split('_')
	for i in range(0, len(newString)):
		if i != 0:
			newString[i] = newString[i][0].upper() + newString[i][1:]
	return ''.join(newString)

def isBad(myString):
	if '_' in myString and '-' in myString:
		return True
	if '--' in myString or '__' in myString:
		return True
	if not myString.lower() == myString and '_' in myString:
		return True
	if not myString.lower() == myString and '-' in myString:
		return True
	return False


import re
find_snake = re.compile('([_])([A-Za-z])')
find_camel = re.compile('([A-Z])')
find_kebab = re.compile('([-])([A-Za-z])')
is_camel = lambda x: x != x.lower()
is_snake = lambda x: '_' in x
is_kebab = lambda x: '-' in x

def to_camel(match):
	return match.group(2).upper()

def to_snake(match):
	return '_' + match.group(1).lower()

def to_kebab(match):
	return '-' + match.group(1).lower()

def change_case(text, target):
	if text == '':
		return ''
	if target not in ['camel', 'kebab', 'snake'] or sum([is_camel(text), is_snake(text), is_kebab(text)]) != 1:
		return None
	if is_camel(text):
		if target == 'snake':
			return re.sub(find_camel, to_snake, text).lower()
		elif target == 'kebab':
			return re.sub(find_camel, to_kebab, text).lower()
	elif is_snake(text):
		if target == 'camel':
			return re.sub(find_snake, to_camel, text)
		elif target == 'kebab':
			return text.replace('_', '-').lower()
	elif is_kebab(text):
		if target == 'snake':
			return text.replace('-', '_').lower()
		elif target == 'camel':
			return re.sub(find_kebab, to_camel, text)
	return text


def discover_case(identifier):
	if identifier is None:
		return None
	elif identifier == '':
		return ''
	elif is_camel(identifier):
		return 'camel'
	elif is_snake(identifier):
		return 'snake'
	elif is_kebab(identifier):
		return 'kebab'
	return None

def is_camel(identifier):
	found_uppercase = False
	for (index, ch) in enumerate(identifier):
		if not ch.isalnum():
			return False
		elif index != 0 and ch.isalpha() and ch.isupper():
			found_uppercase = True
	return found_uppercase

def is_snake(identifier):
	for ch in identifier:
		if ch.isupper():
			return False
	return '_' in identifier and '-' not in identifier

def is_kebab(identifier):
	for ch in identifier:
		if ch.isupper():
			return False
	return '-' in identifier and '_' not in identifier

def split_words(identifier, case):
	if case == 'camel':
		words = []
		word = ''
		for ch in identifier:
			if ch.isupper():
				words.append(word.lower())
				word = ch
			else:
				word += ch
		words.append(word.lower())
		return words
	elif case == 'snake':
		return identifier.split('_')
	elif case == 'kebab':
		return identifier.split('-')
	else:
		return None

def to_camel(words):
	result = ''
	for (index, word) in enumerate(words):
		if index == 0:
			result += word
		else:
			result += word.capitalize()
	return result

def to_snake(words):
	return '_'.join(words)

def to_kebab(words):
	return '-'.join(words)

def change_case(identifier, targetCase):
	case = discover_case(identifier)
	if not case:
		return case
	words = split_words(identifier, case)
	if not words:
		return None
	if targetCase == 'camel':
		return to_camel(words)
	elif targetCase == 'snake':
		return to_snake(words)
	elif targetCase == 'kebab':
		return to_kebab(words)
	else:
		return None


def change_case(identifier, targetCase):
	id = list(str(identifier))
	conv = []
	if identifier.islower() == False and (id.count('-') != 0 or id.count('_') != 0) == True:
		return None
	if id.count('-') != 0 and id.count('_') != 0:
		return None
	if targetCase == 'snake':
		return change_snake(id, conv)
	if targetCase == 'camel':
		return change_camel(id, conv)
	if targetCase == 'kebab':
		return change_kebab(id, conv)
	else:
		return None

def change_snake(id, conv):
	for i in id:
		if i.isupper() == True:
			conv.append('_' + i.lower())
		elif i == '-':
			conv.append('_')
		else:
			conv.append(i)
	return ''.join(conv)

def change_camel(id, conv):
	ids = id.copy()
	ids.insert(0, '.')
	n = 0
	for i in id:
		n = n + 1
		if i == '-' or i == '_':
			continue
		if ids[n - 1] == '-' or ids[n - 1] == '_':
			conv.append(i.upper())
		else:
			conv.append(i)
	return ''.join(conv)

def change_kebab(id, conv):
	for i in id:
		if i == '_':
			conv.append('-')
			continue
		if i.isupper() == True:
			conv.append('-' + i.lower())
		else:
			conv.append(i)
	return ''.join(conv)


import re

def is_snake_case(identifier):
	return bool(re.fullmatch('[a-z_]+', identifier))

def is_camel_case(identifier):
	return bool(re.fullmatch('([a-z]+[A-Z])+[a-z]*', identifier))

def is_kebab_case(identifier):
	return bool(re.fullmatch('[a-z]*-?', identifier))

def is_mixed_case(identifier):
	if '-' in identifier and '_' in identifier:
		return True
	elif '-' in identifier and [c for c in identifier if c.isupper()]:
		return True
	elif '_' in identifier and [c for c in identifier if c.isupper()]:
		return True

def change_case(identifier, targetCase):
	if not identifier:
		return ''
	elif targetCase not in ['kebab', 'camel', 'snake']:
		return None
	elif is_mixed_case(identifier):
		return None
	elif targetCase == 'snake' and (not is_snake_case(identifier)):
		split = [char for char in re.split('([A-Z]|-)', identifier) if char]
		for (i, char) in enumerate(split):
			if char.isupper():
				split[i] = '_' + char.lower()
			elif char == '-':
				split[i] = '_'
		return ''.join(split)
	elif targetCase == 'camel' and (not is_camel_case(identifier)):
		split = re.split('[_-]', identifier)
		return split[0] + ''.join((word.title() for word in split[1:]))
	elif targetCase == 'kebab' and (not is_kebab_case(identifier)):
		split = re.split('([A-Z]|_)', identifier)
		kebab = split[0]
		for (i, word) in enumerate(split[1:]):
			if not word or word == '_':
				kebab += '-'
			elif word.isupper():
				if kebab[-1] == '-':
					kebab += word.lower()
				else:
					kebab += '-' + word.lower()
			else:
				kebab += word.lower()
		return kebab
	else:
		return identifier