【问题标题】:Recursively searching / comparing nested Hashtables?递归搜索/比较嵌套的哈希表?
【发布时间】:2018-07-19 07:25:01
【问题描述】:

首先让我对以下数据结构可能没有正确编写表示歉意。我在代码中动态创建了哈希值,但我不太擅长尝试表示所创建的内容。

$Sailings= @{
    'Arrivals' = @{
        $DynamicKey_booking_Ref = @{
            'GoingTo' = 'Port1';
            'Scheduled' = '09:05';
            'Expected' = '10:09';
            'Status' = 'Delayed'
        }; 
    'Departures' = @{
        $DynamicKey_booking_Ref = @{
            'ArrivingFrom' = 'Port1';
            'Scheduled' = '09:05';
            'Expected' = '09:05';
            'Status' = 'OnTime'
        }; 
    }
}

我通常像这样访问数据。 (希望能确认我使用的结构):

$Sailings.Arrivals.PDH083.GoingTo which returns "Port1"
$Sailings.Arrivals.PDH083.Scheduled which returns "09:05"

在此示例中,PDH083 是基于预订参考动态创建的密钥。

我要做的是将此结构与另一个相同的结构进行比较,但可能具有不同的值。例如。这两个元素一样吗?

$Sailings.Arrivals.PDH083.GoingTo =  "Port1"
$Output.Arrivals.PDH083.GoingTo  = "Port5555"

如果它们不相同,请捕获差异以及不同的路径/键。然后在最后报告他们。

我正在努力编写的是一个递归循环,它可以走到最后一个元素,然后将其与$output 进行比较。虽然我的哈希现在已修复,但我想考虑到以后可能会在较低位置添加更多嵌套哈希的可能性。这是可以轻松完成的事情吗?

我用过

($Sailings.Arrivals.keys | ? {$Output.Arrivals.keys -notcontains $_})

显示丢失的键,但我无法理解为值执行此操作的类似/有效方法。我再次看到我可以使用.values,但它仍然是一次元素。

【问题讨论】:

  • 我认为您将要编写一个查看值的函数,如果它是一个哈希表,它将调用自己,传入两个哈希表进行检查。也许通过[ref] 传递第三个参数来收集所有差异,并通过第四个参数来跟踪值的路径。
  • 感谢 @TheMadTechnician 的 cmets。这就是我一直在想的。今天尝试了几次,但都没有真正奏效。等我回家后我会再去一次。很高兴能坚持到我到达那里。只是想检查一下我是否朝着正确的方向前进。

标签: powershell recursion hashtable


【解决方案1】:

这是一个半递归示例,它返回树中所有叶子的路径的字符串表示形式。希望它能给你一些想法:

$Sailings = @{
   'Arrivals' = @{
        'PDH083' = @{
                        'GoingTo' = 'Port1'
                        'Scheduled' = '09:05'
                        'Expected' = '10:09'
                        'Status' = 'Delayed'
        }
    }
    'Departures' = @{
        'PDH083' = @{
                        'ArrivingFrom' = 'Port1'
                        'Scheduled' = '09:05'
                        'Expected' = '09:05'
                        'Status' = 'OnTime'
        }
    }
}

function Roam($arg, $result="") {
    if(!($arg -is [Hashtable])) {
        return "$result/$arg"
    }

    foreach($pair in $arg.GetEnumerator()) {
        Roam $pair.value "$result/$($pair.key)"
    }
}
Roam $Sailings

if停止条件,你在设计递归操作时首先应该问的问题:什么时候有结果

假设你站在一棵大树的根部,你的任务是映射到那棵树的每一片叶子的路线,从树干到叶子的每一次左转或右转。压倒性的,嗯?但请考虑从无数人中找出一条通往一次离开的路线。

你开始攀爬,到达第一个分支。你会向左还是向右?没关系。你决定取左边的分支,并在纸上写下left。你到达下一个分支,为了好玩,右转,写下right。在几个分支之后,您会携带诸如左、右、右、左、右之类的音符,直到(因为树通常不会产生循环)最终没有更多的攀爬而只有离开以你的名义。

你已经做到了!您将整个(并且只有一个)路径映射到这个单一的叶子,现在可以跳下来(希望树不太高)并代表包含通往您崇拜的朋友的路线的纸。达到休假是停止条件。

但是其他叶子呢?想象一下,你有一种奇怪的克隆自己的超能力。当你到达一个分支时,你克隆了自己和你携带的纸张。如果您向右转,则将右转添加到音符中,但克隆向左,并写下 left。在下一个分支上,您再次克隆自己和纸张,并写下您选择的方向,克隆对其方向也是如此。您不必担心克隆,(也许您自己就是克隆!)只需重复此操作,直到您到达一个离开并且可以跳出来。

$result 参数就是那张纸,最初它不是来自任何地方,它是空的。

因为你的数据结构中的所有叶子都是字符串,你也可以写 if 语句:

if($arg -is [String])

GetEnumerator 怎么样?哈希表通常在 PowerShell 中不排序。我们不能选择第一对或第二对或第六对。但是您的数据结构分支到的方向多于左右方向,因此必须将 Hashtable 订购到 Array 作为人群排队,因此我们可以使用 foreach 循环将我们的克隆发送到他们的路径。 (我们可以用递归替换那个循环,但顺其自然)

所以在函数调用Roam $pair.value "$result/$arg"中,第一个参数是前面的分支,第二个是我们刚刚添加当前方向的纸片。

建议:你不用拼命读完,前几章也很有启发性。 计算机程序的结构与解释 https://mitpress.mit.edu/sicp/full-text/book/book.html

【讨论】:

  • 感谢@JanneTuukkanen 真的很有帮助。我今天回来了,我会看看如何扩展你的 sn-p。
  • 您能否解释一下$results 的填充情况。我可以看到这是有效的,但我不明白 $results 填充了什么?
  • 我假设调用 Roam $Sailings 会使用散列填充 $arg。 IF 块测试看看它是否是一个哈希,如果不是,它然后继续到 foreach,然后循环遍历元素并在该点填充结果? $results 是 GetEnumerator 的保留字吗?在网上看不到任何关于它的东西。抱歉,我显然遗漏了一些东西。
  • 感谢您抽出宝贵时间撰写如此详细的回复并建议您阅读这本书。我一定会看看的。我在很大程度上理解你所解释的内容,我会继续阅读它和建议的链接,直到它吸收:)。我想我只是想理解的是。我没有看到任何实际设置 $result = "Blah" 并想知道实际填充变量 $result 的内容我找到了一个很好的例子,说明我在这里试图实现的目标 [链接] (gist.github.com/edxi/87cb8a550b43ec90e4a89d2e69324806) 再次感谢您为我指明正确的方向
  • 首先$result 是空的。欢迎你用自己的初始值调用函数:Roam $Sailings "Here we go"我为参数添加了一个空字符串的默认值,并写了一点幻想世界的解释。
猜你喜欢
  • 1970-01-01
  • 2016-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-06
  • 2015-01-02
  • 2010-12-06
  • 2013-02-08
相关资源
最近更新 更多