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\$
  • \$\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 '13 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\$ – Slater Victoroff Aug 9 '13 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 '13 at 16:47
  • \$\begingroup\$ @RomanSusi Different strokes for different folks. I'm not going to argue since I already addressed this argument. \$\endgroup\$ – Slater Victoroff Aug 9 '13 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 '13 at 14:56
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
| improve this answer | |
\$\endgroup\$
  • \$\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 '13 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.

| improve this answer | |
\$\endgroup\$
  • \$\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 '13 at 11:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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