Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remap enter to skip some items #87

Closed
grundic opened this issue Aug 15, 2016 · 4 comments
Closed

remap enter to skip some items #87

grundic opened this issue Aug 15, 2016 · 4 comments

Comments

@grundic
Copy link

grundic commented Aug 15, 2016

During iteration through data I would like to skip some items.
As described in docs, it should be possible to skip visiting some elements if return False as a second value from return. But even with such a trivial example I've got an error:

remap({'foo': 'bar'}, enter=lambda p, k, v: ({}, False))
>>> TypeError: expected remappable root, not: {'foo': 'bar'}

Am I doing something wrong or it's a bug?

@mahmoud
Copy link
Owner

mahmoud commented Aug 15, 2016

remap is just letting you know that with that enter, absolutely no remapping would occur. If the very first enter returns False, it's like passing an object that isn't iterable. I can look at improving the error message, perhaps?

@grundic
Copy link
Author

grundic commented Aug 15, 2016

Maybe the error message could be improved, but I don't know how to formulate it better :-/
How then I would avoid entering some items? Does this looks good from your point of view:

def enter(path, key, value):
    # I'd like to skip `baz's`
    if 'baz' in value and isinstance(value, dict):
        value.pop('baz')
    return default_enter(path, key, value)

print(remap({'foo': 'bar', 'baz': 'qux', 'data': ['foo', 'bar', 'baz']}, enter=enter))

>>> {'foo': 'bar', 'data': ['foo', 'bar', 'baz']}

Originally my problem is next: I have a nested dictionaries, which represents hierarchical configuration object. All children should inherit parent properties, they can append to them or override them. And what I need is to transpose this hierarchical structure, so I can ask each node about its properties taking into account parent properties.
Again, here is an example of my current implementation:

import copy

d = {
    'name': 'ROOT',
    'properties': ['foo'],
    'includes': [
        {
            'name': 'nested-1',
            'properties-override': ['bar', 'baz'],
            'includes': [
                {'id': '1'},
                {'id': '2', 'properties': ['quz']},
                {'id': '3'},
            ]
        }
    ]
}


def traverse(data, parents=None):
    result = list()

    if parents is None:
        parents = list()

    if 'id' in data:
        assert 'includes' not in data
        data['parents'] = copy.deepcopy(parents)
        return [data]
    else:
        includes = data.pop('includes', list())
        parents.append(data)

        if not isinstance(includes, list):
            includes = [includes]

        for inc in includes:
            child = traverse(inc, parents)
            result.extend(child)

        parents.pop()
    return result


if __name__ == '__main__':
    for item in traverse(d):
        print(item)

>>> {'id': '1', 'parents': [{'name': 'ROOT', 'properties': ['foo']}, {'properties-override': ['bar', 'baz'], 'name': 'nested-1'}]}
>>> {'id': '2', 'properties': ['quz'], 'parents': [{'name': 'ROOT', 'properties': ['foo']}, {'properties-override': ['bar', 'baz'], 'name': 'nested-1'}]}
>>> {'id': '3', 'parents': [{'name': 'ROOT', 'properties': ['foo']}, {'properties-override': ['bar', 'baz'], 'name': 'nested-1'}]}

Can the same problem be solved with remap functionality?
Sorry, maybe that's too big/complex question for the ticket.

@grundic
Copy link
Author

grundic commented Aug 15, 2016

Okay, looks like I implemented the same traverse method with remap functionality, but I don't know whether it's optimal or not:

import copy
from boltons.iterutils import remap, default_enter, default_exit

d = {
    'name': 'ROOT',
    'properties': ['foo'],
    'includes': [
        {
            'name': 'nested-1',
            'properties-override': ['bar', 'baz'],
            'includes': [
                {'id': '1'},
                {'id': '2', 'properties': ['quz']},
                {'id': '3'},
            ]
        }
    ]
}


def traverse(data=None):
    if not data:
        data = d

    result = list()
    parents = list()

    def enter(path, key, value):
        if 'includes' in value:
            includes = value.pop('includes')
            parents.append(value)
            value = includes

        res = default_enter(path, key, value)
        return res

    def visit(path, key, value):
        if 'id' in value and isinstance(value, dict):
            item = copy.deepcopy(value)
            item['parents'] = copy.deepcopy(parents)
            result.append(item)

        return key, value

    def exit(path, key, old_parent, new_parent, new_items):
        if old_parent == parents[-1]:
            parents.pop()
        res = default_exit(path, key, old_parent, new_parent, new_items)
        return res

    remap(data, visit=visit, enter=enter, exit=exit)
    return result


if __name__ == '__main__':
    for item in traverse(d):
        print(item)

>>> {'id': '1', 'parents': [{'name': 'ROOT', 'properties': ['foo']}, {'properties-override': ['bar', 'baz'], 'name': 'nested-1'}]}
>>> {'id': '2', 'properties': ['quz'], 'parents': [{'name': 'ROOT', 'properties': ['foo']}, {'properties-override': ['bar', 'baz'], 'name': 'nested-1'}]}
>>> {'id': '3', 'parents': [{'name': 'ROOT', 'properties': ['foo']}, {'properties-override': ['bar', 'baz'], 'name': 'nested-1'}]}

@grundic
Copy link
Author

grundic commented Aug 16, 2016

Ah, it's still not perfect, but won't bother you with opened ticket.
Thank you :)

@grundic grundic closed this as completed Aug 16, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants