ianlini / flatten-dict

A flexible utility for flattening and unflattening dict-like objects in Python.
MIT License
176 stars 36 forks source link

Add option to skip enumeration by element types #53

Open Minyus opened 2 years ago

Minyus commented 2 years ago

flatten-dict enumerates all the lists if enumerate_types=(list,) regardless of the element types.

The modification by this Pull Request adds flexibility to enumerate based on the element types while keeping the backward compatibility.

For example, users can now keep list of specific data types (e.g. str) set by keep_element_types argument while still enumerating list of other types.

        >>> flatten({'a': ['b', 'c']}, enumerate_types=(list,), keep_element_types=(str,))
        {('a',): ['b', 'c']}

        >>> flatten({'a': [10, 11]}, enumerate_types=(list,), keep_element_types=(str,))
        {('a', 0): 10, ('a', 1): 11}

        >>> flatten({'a': [{'b': 'foo'}]}, enumerate_types=(list,), keep_element_types=(str,))
        {('a', 0, 'b'): 'foo'}
ianlini commented 2 years ago

Thanks for your contribution. I'm thinking about a more general way to fulfill your requirement because keep_element_types looks too specific to some specific cases. I have 2 alternatives in my mind:

  1. Accept callable in enumerate_types and when checking the type, replace the if isinstance(value, enumerate_types) with something like if any(isinstance(value, t) if isinstance(t, type) else t(parent, value) for t in enumerate_types). Note that I use t(parent, _d) here because I think parent might be useful in some cases.

  2. Add a exclude_enumerate_types and accept the type representation in typing (e.g., List[str]), and use some third-party package to check the type. However, this might not solve all the issue. When there are int and str in the list, it won't be a List[str]. If you want to reject the enumeration in this case, it might be hard to represent that you want to check whether there is at least one str in the list.

ianlini commented 2 years ago

An example for the usage:

>>> flatten({'a': ['b', 'c']}, enumerate_types=(list,), keep_element_types=(str,))

is equivalent to

def is_list_of_nonstr(parent, obj):
    return isinstance(obj, list) and not any(isinstance(val, str) for val in obj)

flatten({'a': ['b', 'c']}, enumerate_types=(is_list_of_nonstr,))
ianlini commented 2 years ago

You can do more interesting things like this:

from some_type_checking_tool import check

def is_list_of_str(parent, obj):
    return check(obj, list[str])

def is_the_specific_key(parent, obj):
    return parent == ("the", "specific", "key")
Minyus commented 2 years ago

Sure, sounds good to me. Could you please implement it?

ianlini commented 2 years ago

Will do it when I have time. Issue created: #54. Thanks.

Minyus commented 2 years ago

Thank you! I look forward to it.