1: #!/usr/bin/perl -w
2: #
3: # treemap BLOCK HASHREF
4: # treemap BLOCK ARRAYREF
5: #
6: # Works like map, for arbitrary nested data structures. Data are
7: # are modified in-place (unlike map). Returns the original reference.
8: # Hash keys are not modified.
9: #
10: # UPDATE: now handles scalar references, and trimmed an unnecessary line
11: # as suggested by dkubb (thanks!)
12: #
13: # Handles cyclical references just fine, thank you.
14: #
15: sub treemap (&$) { &_treemap }
16: sub _treemap {
17: my ($code, $node, $refs) = @_;
18: if (not my $type = ref $node) {
19: local $_ = $node;
20: $node = &$code();
21: }
22: elsif (not exists $refs->{$node}) {
23: undef $refs->{$node}; # sneaky, eh?
24: if ($type eq 'HASH') {
25: $node->{$_} = _treemap($code, $node->{$_}, $refs) for keys %$node;
26: }
27: elsif ($type eq 'ARRAY') {
28: $_ = _treemap($code, $_, $refs) for @$node;
29: }
30: elsif ($type eq 'SCALAR') {
31: $node = \_treemap($code, $$node, $refs);
32: }
33: }
34: $node;
35: }
36:
37: ####################### EXAMPLE #############################
38:
39: $data = {
40: 'nums' => [
41: 'one',
42: 'two',
43: 'three',
44: 'four',
45: [
46: 'five',
47: 'six',
48: [
49: 'seven',
50: 'eight',
51: ]]],
52: 'two' => '2',
53: 'doh' => \'blah blah',
54: 'more' => {
55: 'a' => 'vala',
56: 'b' => 'valb',
57: 'c' => 'valc',
58: 'd' => 'vald'
59: }
60: };
61:
62: use Data::Dumper;
63: print Dumper treemap { "-=\U$_=-" } $data;
64: print Dumper treemap { s/\W/./g; $_ } $data;
65: print Dumper treemap { reverse lc } $data;