4
\$\begingroup\$

This is a function I just wrote that tries to condense a set of strings into grouped lines. It actually works, but looks ugly. Is there a better way to achieve the same thing?

Take 4 filtering empty strings first and dropping the if line on the final yield

def mergeToLines(strings, length=40, sep="    "):
    strs = (st for st in sorted(strings, key=len, reverse=True) if st)
    line = strs.next()
    for s in strs:
        if (len(line) + len(s) + len(sep)) >= length:
            yield line
            line = s
        else:
            line += sep + s
    yield line

The idea is that it takes a bunch of strings, and combines them into lines about as long as lineLength. Each line has one or more of the original strings in it, but how many isn't clear until I actually get to slotting them in. Also, note that if a single entry in the list is longer than lineLength, it's still added as its own line (dropping elements is not an option here).

\$\endgroup\$
  • 1
    \$\begingroup\$ In take 3, if line: is redundant unless you have empty strings in strings. If you do have empty strings, you probably should filter out all of them earlier on. \$\endgroup\$ – Janne Karila Apr 26 '13 at 6:36
  • \$\begingroup\$ @JanneKarila - good point. \$\endgroup\$ – Inaimathi Apr 26 '13 at 13:46
3
\$\begingroup\$

RECURSIVE SOLUTION: NOTE: it assumes that the input strings is already sorted. You could sort this at every call of the function, but this seems like unnecessary overhead.

def recursiveMerge(strings=[], current_line='', line_length=40, result=[]):
    """ Gather groups of strings into a line length of approx. 40 """

    # base case
    if not strings:
        result.append(current_line)
        return result

    else:
        next_line = strings.pop(0)

        # if current_line + next_line < line_length, add them
        # otherwise append current_line to result
        if len(current_line + next_line) < line_length:
            current_line = '    '.join([current_line, next_line]).lstrip(' ')    
        else:
            result.append(current_line)
            current_line = next_line

        # recursively call function
        return recursiveMerge(strings, current_line = current_line, result=result)

ITERATIVE SOLUTION:

def myMerge(strings, line_length=40, res=[]):
    """ Gather groups of strings into a line length of approx. 40 """

    # sort the list of string by len
    strings = sorted(strings, key=len, reverse=True)

    # loop through the group of strings, until all have been used
    current_line = strings.pop(0)
    while strings:
        next_line = strings.pop(0)

        # if current_line + next_line shorter than line_length, add them
        # otherwise, append current_line to the result

        if len(current_line + next_line) < line_length:
            current_line = '    '.join([current_line, next_line])
        else:
            res.append(current_line)
            current_line = next_line

    # add any remaining lines to result     
    res.append(current_line)

    return res
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ l_len should be line_length, no? \$\endgroup\$ – William Morris Apr 26 '13 at 0:59
2
\$\begingroup\$

Your code isn't so ugly, guys :) But I can write it better

def cool_merger(strings, line_len=40, sep="    "):
    ss = list(sorted(strings, key=len, reverse=True))
    return [line for line in [(lambda n, s: s if n+1 < len(ss) and (len(s) + len(ss[n+1]) >= line_len) else (ss.__setitem__(n+1, sep.join((str(ss[n]), str(ss[n+1]))))) if len(ss) > n+1 else ss[n] )(n, s) if len(s) < line_len else s for n, s in enumerate(ss)] if line != None]

(joke)

By the way, works fast.

| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ +1 for it being crazy short! Very nice. Wouldn't want to be your team-mate who had to maintain it though :) \$\endgroup\$ – Nick Burns Apr 26 '13 at 0:19
  • \$\begingroup\$ @NickBurns Thanks :) Surely it's written not for production but just for fun. Maintainability should be on the first place. \$\endgroup\$ – gaart Apr 26 '13 at 6:53

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.