I totally understand. I remember the same boat quite well. That said, the solution, or at least its style, I gave is the only sane way to approach your problem and by far the easiest. Regular expressions can indeed move mountains but it's much harder than you're thinking and for your problem I doubt you (or anyone for that matter) would ever arrive at anything that worked except in the most trivial and inflexible case.

My code was idiomatic but it is easy and straightforward once you can follow it. It's also robust whereas a regex solution will never be. I'll adapt the code a bit to make it more clear and walk you through it. <readmore/> away…

You have a regular data format: JSON. Never use regular expressions when you have a known format with good engines at your disposal. The JSON modules for Perl are tops. You will likely have to install one. JSON::XS is my choice. You can install things even as a beginner and even as a non-admin: Yes, even you can use CPAN. Related: I highly recommend cpanm.

The JSON module(s) turn text into data (read JSON and create Perl data structures) and turn data into text (write Perl data to JSON strings). This is often called marshalling, text -> data, and serializing, data -> text (well, some storage format).

decode_json takes a string of JSON text and returns a Perl data structure. E.g.,

use JSON; my $json = '{ "o": "hai", "hurr": "derp" }'; my $data = decode_json( $json ); print $data->{o}, $/; # Prints "hai"

Reverse it,

print encode_json($data), $/; # Prints: {"hurr":"derp","o":"hai"}

Notice that the JSON Object is not in the same order as the original text and the spacing is different. This is because order (for Objects, not Arrays of course) and whitespace are not relevant. Do not think otherwise. Data formats work a billion times better when they are strictly respected. And this is part of why a regular expression approach here is fail.

# Some plain Perl- my @stuf = ( 1, 2, 3 ); # Or, idiomatically ( 1..3 ) print encode_json(\@stuf), $/; # Need \ to take reference. # Same outcome as [] for array ref plus 1..3- print encode_json( [ 1 .. 3 ] ), $/;

This is a simplistic version of your data. I replaced nulls with values to make example code easier to understand.

{"OWNER":"KeyProjects","age1":"eon","ht1":"moo","wc":"EF1"}

Your requirement appears to be merely to get the age and ht fields into completed and scheduled sub-structures. Simplifying again for brevity here we'll only use scheduled. Means from the simplistic data above, we want this-

{"OWNER":"KeyProjects","scheduled":{"age1":"eon","ht1":"moo"},"wc":"EF +1"}

Code should be pretty easy to follow and, more importantly, to play with now-

use warnings; use strict; use JSON; my $original = '{"OWNER":"KeyProjects","age1":"eon","ht1":"moo"}'; my $desired = '{"OWNER":"KeyProjects","scheduled":{"age1":"eon","ht1" +:"moo"}}'; my $data = decode_json($original); print "The owner is; ", $data->{OWNER}, $/; print "age1 is: ", $data->{age1}, $/; my $age = delete $data->{age1}; print "age1 is now: ", $data->{age1}, $/; # Empty, we deleted it... # ...while putting it into- print "Here I am: $age\n"; # \n = $/, usually/sort-of. my $ht = delete $data->{ht1}; my %sub_hash = ( ht1 => $ht, age1 => $age ); # Perl auto-vivivies data- $data->{scheduled} = \%sub_hash; # \% is like {} print encode_json( $data ), $/; # {"scheduled":{"age1":"eon","ht1":"moo"},"OWNER":"KeyProjects"} # Remember, order is irrelevant in Objects.

That meets your spec. Picking which keys was entirely manual though, so not practical. This is where we go back to the regular expression I used in the first sample: qr/\A (?: age\d+ | ht\d+ ) \z/x;

qr creates a regular expression. The x flag on a regular expression means whitespace doesn't count. \A means start of string, \z means the end. (?:) is non-capturing group and () would work the same here. We group it so we can | (OR) two things between the start and end: age (followed by at least one number) and same for ht. Could also write it (age|ht)\d+.

Now we use the regular expression to decide which keys and their values we'll move to the substructure. Instead of doing them manually like above. I put the nulls back, just because.

use warnings; use strict; use JSON; my $original = '{"OWNER":"KeyProjects","age1":null,"ht1":null}'; my $desired = '{"OWNER":"KeyProjects","scheduled":{"age1":null,"ht1": +null}}'; my $data = decode_json($original); for my $key ( keys %{ $data } ) { print "Found a key: $key", $/; if ( $key =~ /\A (?: age\d+ | ht\d+ ) \z/x ) { print " -> It matches our regular expression\n"; print " -> Deleting it from data structure\n"; my $value = delete $data->{$key}; print " -> Putting it back under sub-structure with key 'sche +duled'\n"; $data->{scheduled}{$key} = $value; } else { print " -> It's fine where it is, no action taken\n"; } } print encode_json( $data ), $/;

Epilog: the Test::More stuff before was just compare the data programmatically so you don't have to eyeball it and hope. To do that here, add on-

use Test::More; my $desired_data = decode_json( $desired ); is_deeply( $desired_data, $data, "Data structures match" ); done_testing(1);

Have fun, HTH, TTFN, etc. :P

Further Reading

Update: </c></c> fixed.


In reply to Re^3: Find a Position and Insert Text by Your Mother
in thread Find a Position and Insert Text by jlb333333

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.