http://www.perlmonks.org?node_id=617570

rootcho has asked for the wisdom of the Perl Monks concerning the following question:

hi,
What I want to do is to store a LoL-path (HoH,AoA,HoA..) and then use it to access the real thing.
Let me give you one example (pseudo code):
%map = ( typeA => {key1}[3]{key2}, typeB => {key3}, typeC => [1]{key4}, )
and then depending on the type I want to access different element of the LoL. i.e. again pseudo code
$lol->{$map{typeA}} <==> $lol->{key1}[3]{key2} $lol->{$map{typeB}} <==> $lol->{key3} $lol->{$map{typeB}} <==> $lol->[1]{key4}
do u have some idea how can i do something like this.
If you didnt understood my question fully, tell me I will try to 'invent' ;) more examples

Replies are listed 'Best First'.
Re: storing and using LoL path
by blazar (Canon) on May 25, 2007 at 19:07 UTC

    What I want to do is to store a LoL-path (HoH,AoA,HoA..) and then use it to access the real thing.
    Let me give you one example (pseudo code):

    %map = ( typeA => {key1}[3]{key2}, typeB => {key3}, typeC => [1]{key4}, )

    Your pseudo code does not shed any light on what you may really want. Indeed it's so far from current perl syntax that it's hard to to say what you mean. In particular on the rhs of => you have something that looks like chained subscripting/dereferencing. But that doesn't make sense in a definition.

    and then depending on the type I want to access different element of the LoL. i.e. again pseudo code

    $lol->{$map{typeA}} <==> $lol->{key1}[3]{key2} $lol->{$map{typeB}} <==> $lol->{key3} $lol->{$map{typeB}} <==> $lol->[1]{key4}

    I now have a very vague idea of what you may want and although probably it's not exactly what you mean, you may be interested in tye's Data::Diver.

Re: storing and using LoL path
by GrandFather (Saint) on May 25, 2007 at 20:56 UTC

    The answer is either simple or complicated, and in either case it is likely that you are asking the wrong question. Provide a little more context for the problem and we may be able to help with a more appropriate solution. Meanwhile:

    In the simple case where the paths are constant and the references in the data structures are not being changed %map simply holds references to the leaf nodes of interest.

    In the complicated case where %map holds templates for access paths where some of the node index values may change you probably need a recursive sub to do the work. In this case it may help if $lol were an object.

    Note that the simple case of using a reference to the desired node is appropriate where you want to avoid repeating the same tiresome access path into a complicated structure many times in the same context. For example:

    my $lol = [[{pigs =>'fly'}, [1, 2, 3], {apples => [qw(red green yellow +)], oranges => [orange]}]]; my $oranges = $lol->[0][2]{oranges};

    Note: code not tested


    DWIM is Perl's answer to Gödel
Re: storing and using LoL path
by almut (Canon) on May 25, 2007 at 21:07 UTC

    If I'm understanding you correctly (which I'm not at all sure), what you want is something like could be achieved with the following if/elsif... structure

    sub lookup { my ($type, $ref) = @_; if ($type eq 'typeA') { return $ref->{key1}[3]{key2}; } elsif ($type eq 'typeB') { return $ref->{key3}; } elsif ($type eq 'typeC') { return $ref->[1]{key4}; } # ... }

    i.e. with the following minimal sample data

    my @LoANY = ( { type => "typeA", data => { key1 => [0,1,2, { key2 => "Value1" } ] } }, { type => "typeB", data => { key3 => "Value2" } }, { type => "typeC", data => [0, { key4 => " Value3" } ] }, # ... );

    this loop

    for my $elem (@LoANY) { print lookup($elem->{type}, $elem->{data}), "\n"; }

    would print

    Value1 Value2 Value3

    But you think the if/elsif thingy is not perlish enough, would not scale decently up to a gazillion of different types, or some such... (?)

    In that case, one other way to do it would be to set up little "accessor" functions (not in the OO sense, thus the quotes), which you would index via the hash, e.g.

    my %map = ( typeA => sub { $_[0]->{key1}[3]{key2} }, typeB => sub { $_[0]->{key3} }, typeC => sub { $_[0]->[1]{key4} }, # ... );

    In that case, you could write the above loop as

    for my $elem (@LoANY) { print $map{$elem->{type}}->($elem->{data}), "\n"; }

    The type would select the appropriate function (via %map), which "knows" how to get at the desired data. The data (i.e.the toplevel ref to some data structure) is passed to the function as argument.

    I'm sure that once you confirm this is what you want to do, other Monks will come up with various other solutions... :)

      Sorry all, for not being too descriptive.. The closest I want to accomplish is the this example u shown :
      my %map = ( typeA => sub { $_[0]->{key1}[3]{key2} }, typeB => sub { $_[0]->{key3} }, typeC => sub { $_[0]->[1]{key4} }, # ... );
      i.e. in my concentrate case I was looping over a result of SOAP::Lite. Which depending on the type of the object places the 'price' in different places f.e.
      $$obj{cost} <--type1 $$obj{priceList}{price} <--type2 $$obj{priceList}{price}{value} <--type3 ...etc..
      So instead of doing alot of if-typeX, I thought it will be good if I can store the access-path in some way and do a simple hash lookup.
      Is it more clear now ;)
      (Something like XPath on LoL)
        The closest I want to accomplish is the this example u shown :
        my %map = ( typeA => sub { $_[0]->{key1}[3]{key2} }, typeB => sub { $_[0]->{key3} }, typeC => sub { $_[0]->[1]{key4} }, # ... );

        In what way does this not do what you described?

        Well, another way would be to store the "XPath" as a string in the hash, and then eval it at runtime, i.e.

        my %map = ( typeA => "{key1}[3]{key2}", typeB => "{key3}", typeC => "[1]{key4}", # ... ); for my $elem (@LoANY) { my $xpath = $map{$elem->{type}}; my $lol = $elem->{data}; print eval "\$lol->$xpath", "\n"; }

        With the example data from my previous post, this would also print

        Value1 Value2 Value3

        In other words, you construct snippets of literal Perl source (e.g. "\$lol->$xpath", interpolating to '$lol->{key1}[3]{key2}'), which you then eval.  Thing is that the XPath thing would need to be turned into runnable code — either at compile-time (like in the function dispatcher approach shown first), or with an eval at runtime.

        Expect a performance penalty though, if you call the eval many many times... (Update: not to mention that using eval would require some awareness of security issues, if you apply it in scenarios where the code being constructed dynamically is potentially coming from insecure sources)

Re: storing and using LoL path
by naikonta (Curate) on May 25, 2007 at 19:01 UTC
    If you didnt understood my question fully, tell me I will try to 'invent' ;) more examples
    That would be helpful, specially for the real examples, along with more description about what are you trying to do what result do you expect. In the mean time, you might want to check Data::Walker, Data::Dref, and/or Data::Match.

    Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

Re: storing and using LoL path
by kyle (Abbot) on May 25, 2007 at 19:15 UTC

    I must not understand what you're looking for here. It seems to me that $lol->{$map{typeA}} is pretty much the same as $lol->{typeA} (as long as $map{typeA} is held constant).

    I might also suggest that the only way you're going to get any $lol to respond sensibly to both $lol->{key1}[3]{key2} (which implies it's a hash ref) and $lol->[1]{key4} (which implies it's an array ref) is to make $lol an object which will use overload to be both things.

    Perhaps you could tell us what you're really trying to do. This sounds a lot like an XY Problem.

      Perhaps you could tell us what you're really trying to do. This sounds a lot like an XY Problem.

      I would rather say an XWTFITL Problem! (Just coined...)

      However after some reading futher along this thread I think that the OP would like to "save" a dereferencing chain as a single "entity": of course strictly speaking this is not possible, but a module like Data::Diver which some of us mentioned could come close in functionality. However this makes me wonder about Perl 6.

Re: storing and using LoL path
by ikegami (Patriarch) on May 28, 2007 at 18:05 UTC