-3

I have a defaultdict of shape:

defaultdict(<function __main__.construct_dicts.<locals>.<lambda>>,
            {1: defaultdict(set, {'A': {0, 1}}),
             2: defaultdict(set, {'A': {1, 3}, 'E': {12, 14}}),
             3: defaultdict(set,
                         {'A': {3, 6},
                          'C': {4, 7, 10},
                          'D': {5, 8, 11},
                          'E': {9, 12}})})

How can I put all the values into three lists, like:

lst_1 = [[0, 1]]
lst_2 = [[1, 3], [12, 14]]
lst_3 = [[3, 6], [4, 7], [7, 10], [5, 8], [8, 11], [9, 12]]

where, for a set of odd values, I repeat the last value in the old pair as the new value in the new pair, for example {5, 8, 11} became [5, 8], [8, 11].

Qubix
  • 4,161
  • 7
  • 36
  • 73
  • Have you tried anything? Can you provide some *reproducible* code to construct the `defaultdict`? – jpp Aug 01 '18 at 08:55
  • I have tried, but the problem is I don't know how to access the values from inside the dict. – Qubix Aug 01 '18 at 08:59

3 Answers3

2

You can use a itertools.zip_longest to generate the value pairs you want, and then use enumerate to index the resulting list so that you can populate the missing values from sets with odd-numbered items from the pair of the last index:

from itertools import chain, zip_longest
d = {
    1: {'A': {0, 1}},
    2: {'A': {1, 3}, 'E': {12, 14}},
    3: {'A': {3, 6}, 'C': {4, 7, 10}, 'D': {5, 8, 11}, 'E': {9, 12}}
}
lst1, lst2, lst3 = [[[i[n - 1][1], j[0]] if j[1] is None else list(j) for n, j in enumerate(i)] for i in [list(chain.from_iterable([list(zip_longest(l[::2], l[1::2])) for l in map(sorted, v.values())])) for v in d.values()]]

This outputs:

[[0, 1]]
[[1, 3], [12, 14]]
[[3, 6], [4, 7], [7, 10], [5, 8], [8, 11], [9, 12]]
blhsing
  • 91,368
  • 6
  • 71
  • 106
1

Since I answered your previous question I think what you want can be achieved using a list comprehension as following:

[list(i.values()) for i in nested_dict.values()]

But note that if your want the results to have an specific order you can't achieve that using aforementioned method because dictionaries and sets are not ordered (except since Python-3.7 that dictionaries are insertion ordered).

Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • This just gives : [dict_values([{0, 1}, set()]), dict_values([{1, 3}, {12, 14}]), dict_values([{3, 6}, {10, 4, 7}, {8, 11, 5}, {9, 12}]), dict_values([set(), set()])] – Qubix Aug 01 '18 at 09:13
  • @Qubix You can convert the `dict_value` object to list then. Check the update. Also note that if you want to convert sets to list which I don't think if it's necessary you can use `list(map(list, i.values()))`. – Mazdak Aug 01 '18 at 09:26
  • The list(map()) seems to work, but can I make the elements appear in the same order as in the dict? Now {4, 7, 10} becomes [10, 4, 7] etc. – Qubix Aug 01 '18 at 09:39
  • Not in a reliable way, unless you use another data structure than sets. You can look for data structures that keep order and unique items, you'll find plenty of methods. – Mazdak Aug 01 '18 at 09:44
1

Edit: Just realized that I mis-read your output; answer updated.

The fact that it's a defaultdict does not really matter, you can just get the values using... well, values(). Note, however, that dict and set are unordered (at least in most versions of Python), so if you want to get that order, you have to sort the different dicts and sets, you you might want to use items() instead of values() to get key/value pairs and sort those by the key. You can do so in a nested list comprehension and then unpack to those three variables, or use the nested list directly. To get pairs of consecutive elements from the inner lists, you can use the zip(lst, lst[1:]) recipe.

d = { 1: {'A': {0, 1}},
      2: {'A': {1, 3}, 'E': {12, 14}},
      3: {'A': {3, 6}, 'C': {4, 7, 10}, 'D': {5, 8, 11}, 'E': {9, 12}}}

def pairs(values):
    return zip(values, values[1:])
l1, l2, l3 = [[p for _, vs in sorted(inner.items())
                 for p in pairs(sorted(vs))]
              for _, inner in sorted(d.items())]
print(lst1, lst2, lst3, sep="\n")
# [(0, 1)]
# [(1, 3), (12, 14)]
# [(3, 6), (4, 7), (7, 10), (5, 8), (8, 11), (9, 12)]

Note that this will sort the items by their value (numerically or lexicographically), not by their insertion order. For the latter, you can use OrderedDict (or Python 3.7+) and then go without sorted.

tobias_k
  • 81,265
  • 12
  • 120
  • 179