【问题标题】:python change value in nested dictionary if condition is met如果满足条件,python在嵌套字典中更改值
【发布时间】:2016-05-06 15:15:39
【问题描述】:

我知道以前已经有人问过类似的问题,但我在实施这些特殊情况时确实遇到了问题:

假设我有一本深度不同的字典,例如:

dicti = {'files':
    {'a':{'offset':100, 'start': 0}, 
     'b':{
         'c':{'offset':50, 'start':0}
         'd':{'offset':70, 'start':0}  
         }
     'e':{
         'f':{'offset':80, 'start':0}
         'g':{'offset':30, 'start':0}  
         'h':{'offset':20, 'start':0}   
         } 
    }
        }
   etc... (with a lot more different levels and entries)

所以现在我想要一份结构和键基本相同的字典副本,但如果'offset'(在任何级别)大于假设50 'offset' 应该更改为0

我想某种迭代函数会是最好的,但我无法理解...

【问题讨论】:

  • 你的数据在被读入字典之前是什么样子的?可能你有更好的python pandas 模块解决方案。
  • 是的,我也不喜欢我的数据,但它是从 json 文件加载的,所以我对此无能为力
  • 有一个函数可以在json 中读取pandas pandas.pydata.org/pandas-docs/stable/generated/… 你可以获得更多的结构化数据来操作。

标签: python dictionary iterator


【解决方案1】:

您可以使用标准机制进行复制,然后修改复制的字典(在我的示例中为解决方案 #1),或者您可以在同一函数中进行复制和修改(解决方案 #2)。

无论哪种情况,您都在寻找递归函数。

import copy
from pprint import pprint
dicti = {'files':
    {'a':{'offset':100, 'start': 0},
     'b':{
         'c':{'offset':50, 'start':0},
         'd':{'offset':70, 'start':0},
         },
     'e':{
         'f':{'offset':80, 'start':0},
         'g':{'offset':30, 'start':0},
         'h':{'offset':20, 'start':0},
         }
    }
}

# Solution 1, two passes
def modify(d):
    if isinstance(d, dict):
        if d.get('offset', 0) > 50:
            d['offset'] = 0
        for k,v in d.items():
            modify(v)
dictj = copy.deepcopy(dicti)
modify(dictj)
pprint(dictj)

# Solution 2, copy and modify in one pass
def copy_and_modify(d):
    if isinstance(d, dict):
        d2 = {k:copy_and_modify(v) for k,v in d.items()}
        if d2.get('offset') > 50:
            d2['offset'] = 0
        return d2
    return d
dictj = copy_and_modify(dicti)
pprint(dictj)

【讨论】:

  • 谢谢我真的很喜欢你的第二个解决方案,就像一个魅力!
  • 请注意,您的示例不包含任何 listtuple 或其他可迭代对象。如果你的真实数据是这样,你可能需要更新我的解决方案。
【解决方案2】:

递归解决方案将更加直观。你想要类似下面的伪代码:

def copy(dict):
    new_dict = {}
    for key, value in dict:
        if value is a dictionary:
            new_dict[key] = copy(value)
        else if key == 'offset' and value > 50:
            new_dict[key] = 0
        else:
            new_dict[key] = value
    return new_dict

【讨论】:

  • 在处理庞大的字典时,这是否比迭代函数慢?
  • for key, value in dict: 不能使用字典。你用示例字典试过了吗?
  • @ccf 这就是我说它是伪代码的原因。因为我没有可以运行伪代码的解释器或编译器(如果你知道,请告诉我),我没有在示例字典上对其进行测试
【解决方案3】:
d = {'files':
    {'a':{'offset':100, 'start': 0},
     'b':{
         'c':{'offset':50, 'start':0},
         'd':{'offset':70, 'start':0}
         },
     'e':{
         'f':{'offset':80, 'start':0},
         'g':{'offset':30, 'start':0},
         'h':{'offset':20, 'start':0}
         }
    }
        }

def transform(item):
    new_item = item.copy()  # consider usage of deepcopy if needed
    if new_item['offset'] == 80:
        new_item['offset'] = 'CHANGED'
    return new_item

def visit(item):
    if item.get('offset'):
        return transform(item)
    else:
        return {k: visit(v) for k, v in item.items()}

result = visit(d)
print(result)

输出:

{
    'files': {
        'b': {
            'd': {
                'offset': 70,
                'start': 0
            },
            'c': {
                'offset': 50,
                'start': 0
            }
        },
        'e': {
            'g': {
                'offset': 30,
                'start': 0
            },
            'h': {
                'offset': 20,
                'start': 0
            },
            'f': {
                'offset': 'CHANGED',
                'start': 0
            }
        },
        'a': {
            'offset': 100,
            'start': 0
        }
    }
}

您可以修改一些关于答案中使用的东西的链接:

【讨论】:

    【解决方案4】:

    一旦满足条件,您可以调用递归函数来更改其值:

    dicti = {'files':
        {'a':{'offset':100, 'start': 0}, 
         'b':{
             'c':{'offset':50, 'start':0},
             'd':{'offset':70, 'start':0}  
             },
         'e':{
             'f':{'offset':80, 'start':0},
             'g':{'offset':30, 'start':0},  
             'h':{'offset':20, 'start':0}   
             } 
        }
    }
    
    
    def dictLoop(dt):
        for k, v in dt.items():
            if isinstance(v, int):
                if k == 'offset' and v > 50:
                    dt[k] = 0
            else: dictLoop(v)
        return dt
    
    print dictLoop(dicti)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-30
      • 2019-07-05
      • 2011-03-10
      • 2019-03-13
      • 1970-01-01
      • 1970-01-01
      • 2019-08-01
      • 1970-01-01
      相关资源
      最近更新 更多