Skip to main content
Consolidate comments into answer.
Source Link
Peilonrayz
  • 43.5k
  • 7
  • 76
  • 155
  • Calling a dictionary a list is very confusing

  • Rather than using min, you could use sorted.

    This'll change the time complexity of your code from \$O(n^2)\$ to \$O(n \log n)\$, as Python uses the Timsort. And since sorted is written in C, it'll be super fast too.

    Sorted also has the keyword argument key, which lets you sort by another value, rather than the value you get.

    This also keeps the original dictionary intact.

  • You can use dict and enumerate to build your input list. And to build a list to pass to enumerate you could use str.split.

And so I'd change your code to:

def sort_dict_by_value_len(dict_):
    return sorted(dict_.items(), key=lambda kv: (len(kv[1]), kv[0]))

def sort_dict_by_value_len_without_key(dict_):
    return [(k, dict_[k]) for _, k in sorted((len(v), k) for (k, v) in dict_.items())]

dict_ = dict(enumerate('one two three four five six seven eight nine ten'.split(), start=1))
for key, value in sort_dict_by_value_len(dict_):
    print(key, value)

If you only want \$O(1)\$ memory usage, then you could use an insertion sort. But keeps the \$O(n^2)\$ time complexity. I also cheated a bit.

  • I used a generator comprehension, rather than a list comprehension to ensure \$O(1)\$ space. Alternately, you could invert the algorithm, and pop from one array to another.
  • I used bisect.insort to remove most of the logic.
def sort_dict_by_value_len(dict_):
    output = []
    while dict_:
        key, value = dict_.popitem()
        bisect.insort(output, ((len(value), key), (key, value)))
    return (i[1] for i in output)
  • Calling a dictionary a list is very confusing

  • Rather than using min, you could use sorted.

    Sorted also has the keyword argument key, which lets you sort by another value, rather than the value you get.

    This also keeps the original dictionary intact.

  • You can use dict and enumerate to build your input list. And to build a list to pass to enumerate you could use str.split.

And so I'd change your code to:

def sort_dict_by_value_len(dict_):
    return sorted(dict_.items(), key=lambda kv: (len(kv[1]), kv[0]))

dict_ = dict(enumerate('one two three four five six seven eight nine ten'.split(), start=1))
for key, value in sort_dict_by_value_len(dict_):
    print(key, value)
  • Calling a dictionary a list is very confusing

  • Rather than using min, you could use sorted.

    This'll change the time complexity of your code from \$O(n^2)\$ to \$O(n \log n)\$, as Python uses the Timsort. And since sorted is written in C, it'll be super fast too.

    Sorted also has the keyword argument key, which lets you sort by another value, rather than the value you get.

    This also keeps the original dictionary intact.

  • You can use dict and enumerate to build your input list. And to build a list to pass to enumerate you could use str.split.

And so I'd change your code to:

def sort_dict_by_value_len(dict_):
    return sorted(dict_.items(), key=lambda kv: (len(kv[1]), kv[0]))

def sort_dict_by_value_len_without_key(dict_):
    return [(k, dict_[k]) for _, k in sorted((len(v), k) for (k, v) in dict_.items())]

dict_ = dict(enumerate('one two three four five six seven eight nine ten'.split(), start=1))
for key, value in sort_dict_by_value_len(dict_):
    print(key, value)

If you only want \$O(1)\$ memory usage, then you could use an insertion sort. But keeps the \$O(n^2)\$ time complexity. I also cheated a bit.

  • I used a generator comprehension, rather than a list comprehension to ensure \$O(1)\$ space. Alternately, you could invert the algorithm, and pop from one array to another.
  • I used bisect.insort to remove most of the logic.
def sort_dict_by_value_len(dict_):
    output = []
    while dict_:
        key, value = dict_.popitem()
        bisect.insort(output, ((len(value), key), (key, value)))
    return (i[1] for i in output)
Source Link
Peilonrayz
  • 43.5k
  • 7
  • 76
  • 155

  • Calling a dictionary a list is very confusing

  • Rather than using min, you could use sorted.

    Sorted also has the keyword argument key, which lets you sort by another value, rather than the value you get.

    This also keeps the original dictionary intact.

  • You can use dict and enumerate to build your input list. And to build a list to pass to enumerate you could use str.split.

And so I'd change your code to:

def sort_dict_by_value_len(dict_):
    return sorted(dict_.items(), key=lambda kv: (len(kv[1]), kv[0]))

dict_ = dict(enumerate('one two three four five six seven eight nine ten'.split(), start=1))
for key, value in sort_dict_by_value_len(dict_):
    print(key, value)