如何进行多键分组的json转换?

时间:2019-04-19 14:48:23

标签: python python-3.x

我需要将平面json模式(MySQL查询的结果)转换为带有两个键的分层json结构。我有一个可行的解决方案,使用itertools groupby,但是我对代码进行了更多的转换(比这更复杂),并且我正在寻找一种更好的方法在Python中实现(我使用3.7)。也许我忽略了Python中的一些基本运算符,它们会减少我的代码行,或者那里有一个更好的库。我读过有关具有groupby操作的熊猫的信息,但其重点是数据分析,而不是像这样的数据转换。在Node.js中,我使用了jsonata,所以我想知道python中是否存在用于json转换的更好的库。

要澄清:我想提高开发效率;我不担心运行时效率,因为我的数据集很小。

示例输入显示在下面的代码示例中,并且输出需要类似于以下内容(2级分组依据并重命名元素):

import tkinter as tk
import os
import platform
import pygame
import time

class window(object):
    def __init__(self):
        self.root = tk.Tk()  # Main window
        self.root.title("SquareScape")
        # self.root.iconbitmap(r'C:\Users\17_es\PycharmProjects\square_puzzle\images\icon.ico')
        self.root.configure(background='#9b9b9b')

        # Large Frame
        self.win_frame = tk.Frame(self.root, width=670, height=520, highlightbackground='#595959', highlightthickness=2)

        # menu (left side)
        self.menu = tk.Frame(self.win_frame, width=150, height=516, highlightbackground='#595959', highlightthickness=2)
        self.menu_label = tk.Label(self.menu, text="Settings", bg='#8a8a8a', font=("Courier", "16", "bold roman"))
        self.mute = tk.Button(self.menu, text="XXXX", font="Courier", bg='#bcbcbc', activebackground='#cdcdcd')

        tk.Button(self.menu, text="<->", command=lambda: setattr(self, "direction", (-self.direction[0], self.direction[1]))).pack()
        tk.Button(self.menu, text="^", command=lambda: setattr(self, "direction", (self.direction[0], -self.direction[1]))).pack()

        # pygame
        self.pygame_frame = tk.Frame(self.win_frame, width=514, height=514, highlightbackground='#595959', highlightthickness=2)
        self.embed = tk.Frame(self.pygame_frame, width=512, height=512,)

        # Packing
        self.win_frame.pack(expand=True)
        self.win_frame.pack_propagate(0)

        self.menu.pack(side="left")
        self.menu.pack_propagate(0)
        self.menu_label.pack(ipadx=60, ipady=2)
        self.mute.pack(ipadx=40, ipady=2, pady=5)

        self.pygame_frame.pack(side="left")
        self.embed.pack()
        #This embeds the pygame window
        os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
        system = platform.system()
        if system == "Windows":
            os.environ['SDL_VIDEODRIVER'] = 'windib'
        elif system == "Linux":
            os.environ['SDL_VIDEODRIVER'] = 'x11'

        self.root.update_idletasks()
        #Start pygame
        pygame.init()
        self.win = pygame.display.set_mode((512, 512))

        self.bg_color = (255, 255, 255)
        self.win.fill(self.bg_color)
        self.pos = 0, 0
        self.direction = 10, 10
        self.size = 40
        self.color = (0, 255, 0)
        self.root.after(30, self.update)

        self.root.mainloop()


    def update(self):

        first_move = True
        pygame.draw.rect(self.win, self.bg_color, self.pos + (self.size, self.size))


        self.pos = self.pos[0] + self.direction[0], self.pos[1] + self.direction[1]


        if self.pos[0] < 0 or self.pos[0] > 512 - self.size:
            self.direction = -self.direction[0], self.direction[1]
            self.pos = self.pos[0] + 2 * self.direction[0], self.pos[1] + self.direction[1]
        if self.pos[1] < 0 or self.pos[1] > 512 - self.size:
            self.direction = self.direction[0], -self.direction[1]
            self.pos = self.pos[0] + self.direction[0], self.pos[1] + 2 * self.direction[1]

        pygame.draw.rect(self.win, self.color, self.pos + (self.size, self.size))
        pygame.display.flip()
        self.root.after(30, self.update)


screen = window()
tk.mainloop()

这是使用itertools提供所需输出的工作代码:

{'researchSubTypeToResolutionCodes': [
  {'researchSubTypeCode': None, 'resolutionTypes': [
    {'resolutionCode': 999991, 'resolutionSubTypeCodes': [99992, 99993]},
    {'resolutionCode': 999995, 'resolutionSubTypeCodes': [99996]}
    ]
  },
  {'researchSubTypeCode': 33533, 'resolutionTypes': [
    {'resolutionCode': 33726, 'resolutionSubTypeCodes': [33730, 33731, 33732, 33774]},
    {'resolutionCode': 33727, 'resolutionSubTypeCodes': [33730, 33731]}
    ]
  },
  {'researchSubTypeCode': 33534, 'resolutionTypes': [
    {'resolutionCode': 33726, 'resolutionSubTypeCodes': [33730]}
    ]
  }
]}

1 个答案:

答案 0 :(得分:1)

我花了两个步骤,但行数却少了很多,从概念上讲,这应该更容易理解。

首先让我们获取所需的数字。这基本上是一个groupby函数。 为了更好地了解其工作原理,请在for循环的末尾添加print语句,例如print(temp_dic)

temp_dic = dict()
for entry in TEST_QRY_DATA:
    if entry["rsch_sub_typ_cd"] not in temp_dic:
        temp_dic[entry["rsch_sub_typ_cd"]] = dict()
    if entry["resl_cd"] in temp_dic[entry["rsch_sub_typ_cd"]]:
        temp_dic[entry["rsch_sub_typ_cd"]][entry["resl_cd"]].append(entry["sub_resl_cd"])
    else:
        temp_dic[entry["rsch_sub_typ_cd"]][entry["resl_cd"]] = [entry["sub_resl_cd"]]
print(temp_dic)

输出:

{
  None: {999991: [99992, 99993], 999995: [99996]}, 
  33533: {33726: [33730, 33731, 33732, 33774], 33727: [33730, 33731]}, 
  33534: {33726: [33730]}
}

现在我们可以添加所需的标签:

final_dict = {'researchSubTypeToResolutionCodes': []}
for researchSubTypeCode, dic in temp_dic.items():
    temp_list = [{'resolutionCode': key, 'resolutionSubTypeCodes': val} for key, val in dic.items()]
    temp_dic = {'researchSubTypeCode': researchSubTypeCode, 'resolutionTypes': temp_list}
    final_dict['researchSubTypeToResolutionCodes'].append(temp_dic)

from pprint import pprint
pprint(final_dict)

输出:

{'researchSubTypeToResolutionCodes': [
    {'researchSubTypeCode': None, 'resolutionTypes': [{'resolutionCode': 999991, 'resolutionSubTypeCodes': [99992, 99993]}, {'resolutionCode': 999995, 'resolutionSubTypeCodes': [99996]}]}, 
    {'researchSubTypeCode': 33533, 'resolutionTypes': [{'resolutionCode': 33726, 'resolutionSubTypeCodes': [33730, 33731, 33732, 33774]}, {'resolutionCode': 33727, 'resolutionSubTypeCodes': [33730, 33731]}]}, 
    {'researchSubTypeCode': 33534, 'resolutionTypes': [{'resolutionCode': 33726, 'resolutionSubTypeCodes': [33730]}]}
]}

您也许可以使用exampleOrderedDictdefaultdict来实现更加动态和递归的解决方案,但要花一些时间才能解决。