gmr / flatdict

Python module for interacting with nested dicts as a single level dict with delimited keys.
https://flatdict.readthedocs.io
BSD 3-Clause "New" or "Revised" License
112 stars 32 forks source link

as_dict() not correctly re-assembling standard dictionary items #16

Closed danieljimeneznz closed 6 years ago

danieljimeneznz commented 6 years ago

Hi,

The as_dict() method does not appear to be working correctly since the changes from v1.2.0 to v2.0.0.

The method correctly handles lists, tuples and sets but does not re-assemble the dictionary for the other types. Please see attached image as proof: screen shot 2018-02-03 at 1 55 53 pm

Also please note that the test_as_dict() test is not correct as this should read: self.assertDictEqual(self.value.as_dict(), self.VALUES)

instead of: self.assertDictEqual(self.value.as_dict(), self.EXPECT)

Cheers.

breathe commented 6 years ago

I'm seeing similar issue --

In [1]: import flatdict

In [2]: flatdict.__version__
Out[2]: '2.0.1'

In [3]: x = flatdict.FlatDict(delimiter=":")

In [4]: items = [{"a": 1}, {"b": 2}]

In [5]: x["foo:bar:items"] = items

In [6]: x
Out[6]: "{'foo:bar:items': "[{'a': 1}, {'b': 2}]"}"

In [7]: x.as_dict()
Out[7]: {'foo:bar:items': [{'a': 1}, {'b': 2}]}

I would've expected as_dict() to return

{"foo": {"bar": {"items": [{"a": 1}, {"b": 2}] } } }
> python --version
Python 3.6.3
> ipython --version
6.1.0
breathe commented 6 years ago

This particular example also doesn't behave correctly with 'FlatterDict' (I believe FlatterDict is old implementation from v1.2.0? I tried upgrading to to 2.x to see if below bug was resolved and am seeing new error in behavior with 2.x ...)

In [1]: import flatdict

In [2]: x = flatdict.FlatterDict()

In [3]: x["a:b:c"] = [{"d": 1}, {"e": 2}]

In [4]: x
Out[4]: "{'a:b:c:0:d': '1', 'a:b:c:1:e': '2'}"

In [5]: x.as_dict()
Out[5]: {'a:b:c': [1, 2]}
breathe commented 6 years ago

I ported the as_dict() implementation from v.1.2 to v.2.0.1 and then hacked on it until it does what I want ... -- This seems to be working for my use case ...

class MyFlatDict(flatdict.FlatterDict):
    def as_nested_dict(self):
        """Return the flat dictionary as a nested dictionary.

        :rtype: dict

        """
        dict_out = {}
        for key in self._values.keys():
            value = self._values[key]
            if isinstance(value, flatdict.FlatDict):
                if value.original_type == list or value.original_type == tuple:
                    out = []
                    for k, v in sorted(value._values.items()):
                        if isinstance(v, flatdict.FlatDict):
                            nested_item = v.as_nested_dict()
                        else:
                            nested_item = v
                        out.append(nested_item)
                    if value.original_type == tuple:
                        out = tuple(out)
                    dict_out[key] = out
                    pass
                elif value.original_type == dict:
                    dict_out[key] = value.as_nested_dict()
            else:
                dict_out[key] = value
        return dict_out
In [2]: x = MyFlatdict()

In [3]: x["a:b:c"] = [{"d": 1}, {"e": 2}]

In [4]: x
Out[4]: "{'a:b:c:0:d': '1', 'a:b:c:1:e': '2'}"

In [5]: x.as_dict()
Out[5]: {'a': {'b': {'c': [{'d': 1}, {'e': 2}]}}}

In [24]: x.as_nested_dict()["a"]["b"]["c"][0]["d"]
Out[24]: 1
danieljimeneznz commented 6 years ago

I went back to using v1.2 as I needed a "FlatterDict" which is what the v1.2 library implements.

breathe commented 6 years ago

@hydroflax perhaps we could get something like my hack added to the official FlatterDict api of version 2 ...? That method seems to work (not sure about performance implications)

gmr commented 6 years ago

It appears to be that the crux of the issue here is that FlatDict.as_dict() is returning the keyspace flattened instead of nested?

I thought this was the original behavior, but I'll double check.

gmr commented 6 years ago

Confirmed there was a deviation in the original behavior, as such, I'll release a 2.1 that reverts to the previous nested behavior.

gmr commented 6 years ago

Ok, the behavior that will show up in the next revision is FlatDict.as_dict will return a nested dict structure and dict(FlatDict) will return a single level dict with the compound keys as the dict keys.

Going to clean up FlatterDict next before the release.

gmr commented 6 years ago

Released 3.0 that addresses the issue. Sorry for any inconvenience.