【问题标题】:Prune empty and singleton arrayrefs from complex Perl data structure从复杂的 Perl 数据结构中修剪空的和单例的数组引用
【发布时间】:2012-06-26 14:35:53
【问题描述】:

我正在尝试整理从 JSON 读取的 Perl 中的大型数据结构。两个典型的元素看起来像这样(在 JSON 中):

[
    [ [ {'payload':'test'} ], [ [ {'payload':'reply'} ], [] ] ],
    [ [ {'payload':'another thread'} ] 
]

我想完全删除该元素底部的空 arrayref,并将每个仅包含单个 hashref 的 arrayref 替换为包含的 hashref。也就是说,结果应该是这样的:

[
    [ {'payload':'test'}, [ {'payload':'reply'} ] ],
    [ {'payload':'another thread'} ]
]

目前我的代码如下:

use v5.12;
use strict;
use warnings;
use JSON::XS;
use Data::Walk;

sub cleanup {
    if (ref $_ eq 'ARRAY') {
        if (scalar(@{$_}) == 0) {
            die 'mysteriously I never reach this branch!';
            while (my ($key,$value) = each @{$Data::Walk::container}) {
                if ($value == $_) {
                    delete ${$Data::Walk::container}[$key]
                }
            }
        } elsif (scalar(@{$_}) == 1 and ref @{$_}[0]) {
            $_ = @{$_}[0];
        } else {
            my $tail = ${$_}[scalar(@{$_})-1];
            if (ref $tail eq 'ARRAY' and scalar(@{$tail}) == 0) {
                $#{$_}--;
            }
        }
    }
}

sub get {
    my $begin = shift;
    $begin = 0 unless $begin;
    my $end = shift();
    $end = $begin + 25 unless $end;
    my $threads;
    {
        local $/;
        open(my $f, '<emails.json');
        $threads = decode_json <$f>;
        close($f);
    }
    $threads = [ @{$threads}[$begin .. $end] ];
    walkdepth(\&eliminate_singleton, $threads);
    return $threads;
}

print JSON::XS->new->ascii->pretty->encode(&get('subject:joke'));

虽然它成功删除了空数组引用,但它未能折叠单例。如何更正此代码以使其可以折叠单例?

【问题讨论】:

  • +1 提出一个好问题。你应该称自己为 Hedgepruner。 ;-)
  • 如果您发布一个演示该问题的程序,这将非常有帮助。就目前而言,代码运行一个程序notmuch,查询subject:joke以生成JSON数据。这些东西对于问题来说是微不足道的,对于那些试图帮助你的人来说是未知的,并且需要大量的工作来分解你的代码并将相关部分与不相关部分区分开来。您展示了一些数据很好,但就目前而言,它是无效的 JSON,并且查看如何在您的程序中使用它并非易事。结果是答案数量非常少。

标签: perl data-structures perl-data-structures traversal


【解决方案1】:

我看到您想删除作为数组元素的空数组,但我不明白将每个单例 arrayref 替换为对其元素的引用。您是否想用其内容替换作为单元素数组的每个哈希值?

所以

[
  "data1",
  [],
  "data3",
]

转换为

[
  "data1",
  "data3",
]

{
  "key1" : ["val1", "val2"],
  "key2" : ["val3"],
  "key3" : ["val4", "val5"],
}

转换为

{
  "key1" : ["val1", "val2"],
  "key2" : "val3",
  "key3" : ["val4", "val5"],
}

在您的程序中,后者对应于 "tags" : ["inbox"] 变为 "tags" : "inbox"

如果是这种情况,那么这个版本的eliminate_singleton 可以满足您的需求。

它从容器节点获取视图并检查内部是否需要修改。从节点本身的角度来看这样做可能会导致节点在被扫描时被修改,这将破坏程序。事实上,从数组末尾向后循环是安全的,因为它不会删除任何未访问的节点。

use Scalar::Util 'reftype';

sub eliminate_singleton {

  my $node = $_;
  my $type = reftype $node // '';

  if ($type eq 'ARRAY') {
    for (my $i = $#$node; $i >= 0; $i--) {
      my $subnode = $node->[$i];
      my $subtype = reftype($subnode) // '';
      delete $node->[$i] if $subtype eq 'ARRAY' and @$subnode == 0;
    }
  }
  elsif ($type eq 'HASH') {
    for my $k (keys %$node) {
      my $subnode = $node->{$k};
      my $subtype = reftype($subnode) // '';
      if ($subtype eq 'ARRAY' and @$subnode == 1) {
        $node->{$k} = $node->{$k}[0];
      };
    }
  }
}

【讨论】:

  • 不,不是散列元素,数组元素。每个消息表示,一个 hashref,恰好包含在一个无关的 1 元素 arrayref 中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多