3
\$\begingroup\$

I'm looking to let people define arbitrarily nested lists of lists and return a random json document for them. Right now the assumption is that if you have a structure like this:

[item1, item2, [item3, item4]]

The desired document is:

{item1: value,
item2: {
    item3: value,
    item4: value
    }
}

This is my current solution:

def random_document(document_layout, type_, **kwargs):
    document_layout.append("dummy_item")
    doc = lambda l:{l[i]:doc(l[i+1]) if isinstance(l[i+1],list) else random_item(type_,**kwargs) for i in range(len(l)-1)}
    return doc(document_layout)

as a one line fetishist, I feel like the middle line is beautiful, but I think the general hack of adding a dummy element to the end is hackish. Any thoughts on how to make this more elegant?

Also, not looking for deeply readable, this is just for my own private use and isn't going to spread out in any meaningful way, so I would just like to preempt the commentary about it not being readable.

Here's a more "readable" version:

def random_document(document_layout, type_, **kwargs):
    document_layout.append("dummy_item")
    full_dict = {}
    def internal_comprehension(document):
        for i in range(len(document)-1):
            if isinstance(document[i+1], list):
                full_dict.update({document[i]: internal_comprehension(document[i+1])})
            else:
                full_dict.update({document[i]: random_item(type_, **kwargs)})
    return internal_comprehension(document_layout)
\$\endgroup\$
5
  • \$\begingroup\$ Your understanding of elegant code is so different, that I can't improve your solution. However, variable named 'type' is not a good idea, because it shadows built-in function type(). \$\endgroup\$
    – Roman Susi
    Aug 9, 2013 at 14:37
  • \$\begingroup\$ @RomanSusi It's not my understanding of elegant that's different, it's my understanding of readable. The solution for the problem is elegant regardless, the difference is that most people wouldn't find it as readable as I do. \$\endgroup\$ Aug 9, 2013 at 14:44
  • \$\begingroup\$ Perhaps in Python elegance can't be achieved without readability unless you are participating in shortest program contest. Thanks for the normal version. \$\endgroup\$
    – Roman Susi
    Aug 9, 2013 at 16:47
  • \$\begingroup\$ @RomanSusi Different strokes for different folks. I'm not going to argue since I already addressed this argument. \$\endgroup\$ Aug 9, 2013 at 16:49
  • \$\begingroup\$ Could you give a example of how random_document is called? I can't see how you can include a list in the layout and not end up with an unhashable type: list exception. \$\endgroup\$
    – D.Shawley
    Aug 10, 2013 at 14:56

2 Answers 2

3
\$\begingroup\$

Your one-liner is too unreadable for my taste. Furthermore, neither version actually works.

The fundamental obstacle to elegance is that the problem requires you to look ahead one element to interpret how to handle the current element.

def random_document(document_layout, type_, **kwargs):
    def internal_comprehension(document):
        full_dict = {}
        for i in range(len(document)):
            if isinstance(document[i], list):
                # This item should have already been processed in a previous
                # recursive call to internal_comprehension()
                pass
            elif i == len(document) - 1 or not isinstance(document[i+1], list):             
                # This item is an independent item
                full_dict.update({document[i]: random_item(type_, **kwargs)})               
            else:
                # Next item is a list, so create a level of nesting
                full_dict.update({document[i]: internal_comprehension(document[i+1])}) 
        return full_dict
    return internal_comprehension(document_layout)

On the other hand, if you iterate through the document_layout backwards, then you don't have a look-ahead problem. Here's how it could look like:

def random_document(document_layout, type_, **kwargs):
    dict = {}
    iter = reversed(document_layout)
    for k in iter:
        if isinstance(k, list):
            dict[iter.next()] = random_document(k, type_, **kwargs)
        else:
            dict[k] = random_item(type_, **kwargs)
    return dict
\$\endgroup\$
1
  • \$\begingroup\$ You can remove isinstance and use duck-typing: try: dict[k] = random_item(type_, **kwargs) except TypeError: dict[iter.next()] = random_document(k, type_, **kwargs) which uses the fact that lists aren't hashable. \$\endgroup\$
    – Bakuriu
    Sep 10, 2013 at 11:23
0
\$\begingroup\$

The only idea, which comes to my mind, is to replace

for i in range(len(document)-1)

with

for (l, l1) in zip(document, document[1:])
# or izip from itertools

to get pairs of consecutive items.

\$\endgroup\$
1
  • \$\begingroup\$ Do not use l(lowercase L) as a variable name. Also, it doesn't make much sense to use itertools.izip in that code, since document[1:] is already copying the list. Use iterable = iter(document); for a, b in zip(iterable, iterable):. Or you can use itertools.islice to get two iterables on even and odd indexes and zip them together. \$\endgroup\$
    – Bakuriu
    Sep 10, 2013 at 11:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.