2

I've tried many different versions of using push and splice, but can't seem to combine two hashes as needed. Trying to insert the second hash into the first inside the 'Item' array:

(
  ItemData => { Item => { ItemNum => 2, PriceList => "25.00", UOM => " " } },
)
(
  Alternate => {
    Description  => "OIL FILTER",
    InFile       => "Y",
    MfgCode      => "FRA",
    QtyAvailable => 29,
    Stocked      => "Y",
  },
)

And I need to insert the second 'Alternate' hash into the 'Item' array of the first hash for this result:

(
  ItemData => {
    Item => {
      Alternate => {
        Description  => "OIL FILTER",
        InFile       => "Y",
        MfgCode      => "FRA",
        QtyAvailable => 29,
        Stocked      => "Y",
      },
      ItemNum => 2,
      PriceList => "25.00",
      UOM => " ",
    },
  },
)

Can someone suggest how I can accomplish this?

Borodin
  • 126,100
  • 9
  • 70
  • 144
rwfitzy
  • 413
  • 5
  • 16
  • This looks suspiciously like XML. Is it? – Sobrique Dec 14 '16 at 15:11
  • I was going to say the same @Sobrique. Probably XML::Simple. – simbabque Dec 14 '16 at 15:12
  • Oh yeah, the hash is generated from XML pulled via HTTP::Request in an existing application. I'm being asked to add a different format via TCP connection. So I'm trying to mimic the XML hash to passing off to the function that uses the hash. – rwfitzy Dec 14 '16 at 15:17
  • 2
    OK. My suggestion would be to backtrack, and don't do that. `XML::Simple` is a road to pain. If you post the `XML`, I can show you an example that'll splice it together without the data loss inherent in converting back and forth. – Sobrique Dec 14 '16 at 15:40
  • 2
    ***Never ever use `XML::Simple`*** if you can possibly avoid it, and even if you can't you should ask for danger money. The module's own documentation says *"You really don't want to use this module in new code"* and *"The use of this module in new code is discouraged"*. Please read [*Why is XML::Simple "Discouraged"?*](http://stackoverflow.com/questions/33267765/why-is-xmlsimple-discouraged?) – Borodin Dec 14 '16 at 16:05
  • *"the hash is ... pulled via `HTTP::Request` in an existing application"* That's the wrong way to look at it. `HTTP::Request` implements a Perl class that describes an HTTP request message. It won't do very much on its own. You could say that your code uses `LWP::UserAgent`, or even just `LWP`, to fetch the XML data. But the essential information is that you have used `XMLin` from the dreadful `XML::Simple` to process the XML document into a Perl data structure. – Borodin Dec 14 '16 at 16:23
  • 1
    While `XML::Simple` is poor when *reading* an XML document, it is far far worse using `XMLout` to create one. Since that is essentially your requirement, I believe you should adopt [XML::Twig](https://metacpan.org/pod/XML::Twig) instead. The API is extensive, but then XML data isn't as simple as `XML::Simple` tries to make out. There is also `XML::LibXML`. It is less approachable, but the author of `XML::Simple` has written [*Perl XML::LibXML by Example*](http://grantm.github.io/perl-libxml-by-example/) which helps to smooth the transfer. – Borodin Dec 14 '16 at 16:27
  • I'm not using XML::Simple guys, but the application I'm working on does. I am retrieving data via IO::Socket TCP connection into a Perl data structure mimicking the same data structure of another function previously written by the former application developer who did use XML::Simple. So I can pass to the same code theirs was passed to, which does something with the array. Does that make sense? – rwfitzy Dec 14 '16 at 17:27
  • @rwfitzy: When people say *"the application I'm working on"*, they generally mean the program that they're writing. If that's the case then you can use whatever library you like. But you also say *"I am retrieving data ... into a Perl data structure* which tells us nothing about the nature of the data that is arriving via the socket. I can't understand the rest of your post, but it would help if you opened a new question and avoided relative pronouns. You should *name* "the same code", "their code" and "the same data structure" for us to understand. – Borodin Dec 14 '16 at 21:17

1 Answers1

3

Assuming you have two hash references, this is straight-forward.

my $item = {
    'ItemData' => {
        'Item' => {
            'PriceList' => '25.00',
            'UOM'       => ' ',
            'ItemNum'   => '2'
        }
    }
};

my $alt = {
    'Alternate' => {
        'MfgCode'      => 'FRA',
        'Description'  => 'OIL FILTER',
        'Stocked'      => 'Y',
        'InFile'       => 'Y',
        'QtyAvailable' => '29'
    }
};

$item->{ItemData}->{Item}->{Alternate} = $alt->{Alternate};

The trick here is not to actually merge $alt into some part of $item, but to only take the specific part you want and put it where you want it. We take the Alternate key from $alt and put it's content into a new Alternate key inside the guts of $item.

Adam Millerchip pointed out in a hence deleted comment that this is not a copy. If you alter any of the keys inside of $alt->{Alternative} after sticking it into $item, the data will be changed inside of $item as well because we are dealing with references.

$item->{ItemData}->{Item}->{Alternate} = $alt->{Alternate};
$alt->{Alternate}->{InFile} = 'foobar';

This will actually also change the value of $item->{ItemData}->{Item}->{Alternate}->{InFile} to foobar as seen below.

$VAR1 = {
          'ItemData' => {
                          'Item' => {
                                      'ItemNum' => '2',
                                      'Alternate' => {
                                                       'Stocked' => 'Y',
                                                       'MfgCode' => 'FRA',
                                                       'InFile' => 'foobar',
                                                       'Description' => 'OIL FILTER',
                                                       'QtyAvailable' => '29'
                                                     },
                                      'UOM' => ' ',
                                      'PriceList' => '25.00'
                                    }
                        }
        };

References are supposed to do that, because they only reference something. That's what's good about them.

To make a real copy, you need to dereference and create a new anonymous hash reference.

#                                        create a new ref 
#                                          deref
$item->{ItemData}->{Item}->{Alternate} = { %{ $alt->{Alternate} } };

This will create a shallow copy. The values directly inside of the Alternate key will be copies, but if they contain references, those will not be copied, but referenced.


If you do want to merge larger data structures where more than the content of one key needs to be merged, take a look at Hash::Merge instead.

Community
  • 1
  • 1
simbabque
  • 53,749
  • 8
  • 73
  • 136
  • 1
    @AdamMillerchip: I understand that you weren't "proving a point", which implies something more combative. I should probably have used "demonstrate". Your post would be a valuable comment right here without the associated code and output. – Borodin Dec 14 '16 at 16:38
  • 1
    As Adam wrote in his deleted answer, you should point out the danger of shallow copies. You should also probably credit him for the nudge. – Borodin Dec 14 '16 at 16:42
  • @Borodin better? It's pretty long now, and dealing more with refs than with the problem the OP tried to solve. I don't actually think they'll run into this as it seems they're doing a one-off thing. – simbabque Dec 14 '16 at 16:57
  • Hmm. I didn't use "better" anywhere did I? I have upvoted your answer, but I think it could do with the usual warnings about copying references. Adam's deleted post would make a useful comment. – Borodin Dec 14 '16 at 20:59
  • 1
    Ah, sorry :-s If you mean in the sense of "(Is that) better" then yes, that's ideal! Well done. – Borodin Dec 14 '16 at 21:22
  • Yeah I deleted the comment/answer partly in frustration at having most of my answer edited away, partly because I decided it was probably too much of a distraction from the original question. Maybe I should just make the comment again and pretend none of this ever happened? xD – Adam Millerchip Dec 15 '16 at 02:21
  • @simbabque your example for `InFile` shows `111` but it should actually be `foobar`. – Adam Millerchip Dec 15 '16 at 02:31
  • @adam since I credited you we can already pretend it never happened. I usually remove my own comments about missing stuff once the author of the answer fixes the problem. Thanks for pointing out the inconsistency, I figured the string would be more obvious but didn't run the program again. :-) – simbabque Dec 15 '16 at 06:42