Skip to content

Instantly share code, notes, and snippets.

@tanbro
Last active November 16, 2021 07:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tanbro/fd0424d05c8996aca22644564bb0288b to your computer and use it in GitHub Desktop.
Save tanbro/fd0424d05c8996aca22644564bb0288b to your computer and use it in GitHub Desktop.
A python module snakelize or camelize string keys inside a dictionary
"""
Change dictionary's key naming style
"""
from typing import Iterable, Mapping
import stringcase
__all__ = ['convert', 'camelize', 'pascalize', 'snakelize']
STRINGCASE_FUNCTION_MAP = {
'alphanum': stringcase.alphanumcase,
'backslash': stringcase.backslashcase,
'camel': stringcase.camelcase,
'capital': stringcase.capitalcase,
'const': stringcase.constcase,
'dot': stringcase.dotcase,
'lower': stringcase.lowercase,
'pascal': stringcase.pascalcase,
'path': stringcase.pathcase,
'sentence': stringcase.sentencecase,
'snake': stringcase.snakecase,
'spinal': stringcase.spinalcase,
'title': stringcase.titlecase,
'trim': stringcase.trimcase,
'upper': stringcase.uppercase,
}
_ITERABLE_SCALAR_TYPES = (str, bytes, bytearray, memoryview)
def _recursiveable(obj):
if isinstance(obj, Mapping):
return True
if isinstance(obj, Iterable) and not isinstance(obj, _ITERABLE_SCALAR_TYPES):
return True
return False
def convert(obj, case: str, inplace=False):
"""Recursively covert dictionary's string key naming style inside `obj`
When `obj` is
* `dictionary`: Convert it's keys naming style, if the key is string.
* `list`: Iterate the list, and try to convert every item if it is a dictionary.
* `str`: Simply convert it's naming style. `inplace` argument is ignored in this case.
And do above recursively.
Parameters
----------
obj:
Any Mapping or Iterable object with dictionaries in it.
case: str
The naming style convert to. Usually `"snake"`, `"camel"`, `"pascal"`
inplace: bool
Whether to perform an in-place convertion.
Returns
-------
Converted object
"""
_f = STRINGCASE_FUNCTION_MAP[case]
if isinstance(obj, Mapping):
if inplace:
key_pairs = [
(k, _f(k))
for k in obj.keys()
if isinstance(k, str)
]
for k0, k1 in key_pairs:
if k0 == k1:
continue
v = obj.pop(k0) # type: ignore
if _recursiveable(v):
obj[k1] = convert(v, case, inplace) # type: ignore
else:
obj[k1] = v # type: ignore
else:
return {
_f(k) if isinstance(k, str) else k: convert(v, case, inplace) if _recursiveable(v) else v
for k, v in obj.items()
}
elif isinstance(obj, Iterable) and not isinstance(obj, _ITERABLE_SCALAR_TYPES):
if inplace:
for i, v in enumerate(obj):
if _recursiveable(i):
obj[i] = convert(v, case, inplace) # type: ignore
else:
return [
convert(i, case, inplace) if _recursiveable(i) else i
for i in obj
]
elif isinstance(obj, str):
return _f(obj)
return obj
def camelize(obj, inplace=False):
return convert(obj, 'camel', inplace)
def pascalize(obj, inplace=False):
return convert(obj, 'pascal', inplace)
def snakelize(obj, inplace=False):
return convert(obj, 'snake', inplace)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment