0

I have a nested list holding dictionaries as mapping table using a tuple as key. I am struggling to zip the dictionary together so it can be exported by Pandas to csv file:

l = [{('A', 'B'): 1}, {('A', 'C'): 2}, {('A', 'D'): 3}]


def dynDictCombiner(item):
    # would lambda be faster?
    def _combine(item):
        key = item.keys()[0]
        return key, item[key]

    col_keys = ('start', 'stop')
    row_keys = ('value')

    # syntax error
    l = [dict(zip(col_keys + row_keys, k + v)) for ((k, v) :=_combine(item) in l)]
    print(l)


l = dynDictCombiner(l)
#import pandas as pd
#df = pd.DataFrame.from_dict(l)
#print(df.shape)
#print(df)
#df.to_csv(path_or_buf='C:/temp/foo.csv', index=False, header=True)

Expected Output:

[
{'start': 'A', 'stop': 'B', 'value': 1},
{'start': 'A', 'stop': 'C', 'value': 2},
{'start': 'A', 'stop': 'D', 'value': 3}
]

Edit, function without walrus:

def dynDictCombinerSimple(items):
    # would lambda be faster?
    def _combine(item):
        key = list(item.keys())[0]
        return key, (item[key], )

    col_keys = ('start', 'stop')
    row_keys = ('value', )

    result = []
    for item in items:
        k, v = _combine(item)
        result.append(dict(zip(col_keys + row_keys, k + v)))
    print(result)

Out as expected:

[{'start': 'A', 'stop': 'B', 'value': 1}, {'start': 'A', 'stop': 'C', 'value': 2}, {'start': 'A', 'stop': 'D', 'value': 3}]
MafMal
  • 71
  • 5
  • What exactly is the problem with the code you have shown? – mkrieger1 Oct 28 '22 at 18:41
  • I dont know where to put the walrus operator, most often (the tutorials I looked at) it is used in `[... for x in item if (x:= ...]` here I dont have a condition. The code I've posted results in a syntax error. – MafMal Oct 28 '22 at 18:43
  • you should probably add an expected output – Ahmed AEK Oct 28 '22 at 18:45
  • 2
    What would the code look like without using the walrus operator? – mkrieger1 Oct 28 '22 at 18:46
  • Even if you get past this walrus issue, your next one will be a `TypeError: 'dict_keys' object is not subscriptable` inside `_combine()`, and it's anyone's guess as to how you expect this to behave. It would be helpful to see your expected vision of `l`. – Basil Oct 28 '22 at 18:50
  • Why do you try to grab the first key during `_combine()`? Does each dictionary in `l` actually contain multiple items, and you're only interested in the first? – Basil Oct 28 '22 at 18:55

2 Answers2

3

You don't need a walrus operator for this:

l = [{('A', 'B'): 1}, {('A', 'C'): 2}, {('A', 'D'): 3}]

output= [{'start': start, 'stop': stop, 'value': value}
         for dct in l
         for (start, stop), value in dct.items()]

print(output)
# [{'start': 'A', 'stop': 'B', 'value': 1},
#  {'start': 'A', 'stop': 'C', 'value': 2},
#  {'start': 'A', 'stop': 'D', 'value': 3}]
j1-lee
  • 13,764
  • 3
  • 14
  • 26
  • Yes, that just works for key/tuple holding 2 values. We got multiple structures holing a different amout of values. I wanted to have a generic solution. – MafMal Oct 28 '22 at 18:59
  • @MafMal Generic in what sense? In that the length of "col" can be larger? How about "row"? Can it be of length longer than 1? – j1-lee Oct 28 '22 at 19:01
  • Yes: Could be `('A', 'B')` or `('A', 'B', 'C')`, actually row is a dictionary, I wanted to 'present' a minimal reproducable example. – MafMal Oct 28 '22 at 19:05
  • 1
    @MafMal So can `row_keys` have length longer than 1, or is it actually `row_keys = "value"` is always fine? What if the former is the case? Do you want to take cartesian product of `col_keys` and `row_keys` in your output? The problem is that I don't understand what your `_combine` is doing, and so I can't predict what your expected output will be like. – j1-lee Oct 28 '22 at 19:07
1

@MafMal: Please add all relevant information to the question upfront, so it's easier to understand, right now the most relevant information are in the comments.

I interpret an answer to your question like this:

l2 = [
    # rows are dicts as mentioned in the comments
    {
        ('A', 'B', 'C'): {
            'foo': 'bar1'
        }
    }, {
        ('A', 'C', 'D'): {
            'foo': 'bar2'
        }
    }, {
        ('A', 'D', 'E'): {
            'foo': 'bar3'
        }
    }
]

def genericDictCombiner(items, col_keys=('start', 'stop')):
    result = []
    for item in items:
        # As there is just one key/value pair, 
        # possibly that's why you came up with that _combine thing!
        for k, v in item.items():
            resDct = dict(zip(col_keys, k))
            resDct.update(v)
            result.append(resDct)
    return result
 

l = genericDictCombiner(l2)
print(l)

Out:

[{'start': 'A', 'stop': 'B', 'foo': 'bar1'}, {'start': 'A', 'stop': 'C', 'foo': 'bar2'}, {'start': 'A', 'stop': 'D', 'foo': 'bar3'}]
Maurice Meyer
  • 17,279
  • 4
  • 30
  • 47