Re: Joining an array
by roboticus (Chancellor) on Feb 11, 2012 at 00:29 UTC
|
$ cat foo.pl
use strict;
use warnings;
use List::MoreUtils qw(natatime);
my @t = qw(name John number 7 status unknown);
my @u;
my $it = natatime 2, @t;
while (my @vals = $it->()) {
push @u, join("=",@vals);
}
print join("&",@u),"\n";
$ perl foo.pl
name=John&number=7&status=unknown
...roboticus
When your only tool is a hammer, all problems look like your thumb. | [reply] [d/l] [select] |
|
Thanks roboticus. I have seen that module mentioned a number of times, will look into it. I also remember spending many hours building an iterator a la Mark Jason Dominus in Higher Order Perl -- Chapter 4: Iterators. You have just shown me a use for my labour.
| [reply] |
Re: Joining an array
by BrowserUk (Patriarch) on Feb 11, 2012 at 00:37 UTC
|
use List::Util qw[ reduce ];
my $ff=1;
print reduce{
$a .= ( ( $ff ^= 1 ) ? '&' : '=' ) . $b
} qw(name John number 7 status unknown);;
name=John&number=7&status=unknown
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
| [reply] [d/l] |
|
I'm going to show that to my girlfriend.
| [reply] |
|
Dear Sir,
Please be advised that, in keeping with current trends in social networking site norms, we unilaterally, and bindingly, and in absentis of imprimis auctorita, make the highlighted changes to our terms of service. This change to our entirely one-sided, but legally binding contract1, is made with immediate effect, and will be applied retro-actively since the dawn of time2.
7. Limitation of Liability
You understand and agree that BrowserUk and any of its subsidiaries or affiliates shall in no event be liable for any direct, indirect, incidental, consequential, or exemplary damages. This shall include, but not be limited to damages for loss of profits, business interruption, business reputation or goodwill, loss of programs or information, loss of goodwill and/or sexual favours. be they pre-marital, marital or extra-marital, being temporary or permanent, or other intangible loss arising out of the use of or the inability to use the service, or information, or any permanent or temporary cessation of such service or access to information, or the deletion or corruption of any content or information, or the failure to store any content or information.
The above limitation shall apply whether or not BrowserUk has been advised of or should have been aware of the possibility of such damages. In jurisdictions where the exclusion or limitation of liability for consequential or incidental damages is not allowed the liability of BrowserUk is limited to the greatest extent permitted by law.
1Unless you have the cash to take on a corporation with $60 billion in the bank.
2a.k.a: !991.
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
| [reply] |
|
|
|
| [reply] |
Re: Joining an array
by tobyink (Canon) on Feb 11, 2012 at 00:44 UTC
|
use Modern::Perl;
my @t = qw(name John number 7 status unknown);
my $str = '';
while (@t) {
$str .= sprintf('%s=%s', shift @t, shift @t);
$str .= '&' if @t;
}
say $str;
Though if what you're trying to do is construct a query string for a URI, you want to use URI::Escape, which will escape any special characters in your string. (For example, what if one of the strings in your array already contains an equals sign?)
use Modern::Perl;
use URI::Escape qw/uri_escape/;
my @t = qw(name John number 7 status unknown);
$t[1] = 'John Smith'; # something that needs escaping
my $str = '';
while (@t) {
$str .= sprintf(
'%s=%s',
uri_escape(shift @t),
uri_escape(shift @t),
);
$str .= '&' if @t;
}
say $str;
It's a shame you said that the order matters. If it didn't, then casting your array to a hash would be a neat trick:
use Modern::Perl;
use URI::Escape qw/uri_escape/;
my @t = qw(name John number 7 status unknown);
my %th = @t; # cast to hash
my $str = join '&',
map { sprintf '%s=%s', uri_escape($_), uri_escape($th{$_}) }
keys %th;
say $str;
| [reply] [d/l] [select] |
|
Didn't you notice that the keys are in alfabetical order? If you just sort the keys your casting trick will work.
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James
| [reply] [d/l] |
|
I take care of the escaping at an earlier stage (I should have put & in there as that is what I actually use but didn't format it properly in the post). The sprintf syntax has always confused me but I can see clearly here how it works. Very nice. Just wondering how concatenation compares with the other methods above performance wise as I'll be doing this up to a thousand times a go. I'll go and benchmark it.
| [reply] [d/l] |
|
Generally speaking I prefer sprintf over interpolation ("$foo=$bar") except in very trivial cases.
When you need to put $foo and $bar into a string, interpolation is fine. But if you need to put $foo->[1]{name} and encode_entities($bar->get_url("print")) into a string, sprintf looks much better:
sprintf(
'<a href="%s">%s (printable version)</a>',
encode_entities($bar->get_url("print")),
$foo->[1]{name},
);
Bear in mind that if you're outputting this URL in HTML, there are two types of escaping you need; and you need to do them both at the appropriate stage.
use Modern::Perl;
use URI::Escape qw/uri_escape/;
use HTML::HTML5::Entities qw/encode_entities/;
my @t = qw(name John number 7 status unknown);
my %th = @t; # cast to hash
my $url = 'show_person.cgi?' .
join '&',
map { sprintf '%s=%s', uri_escape($_), uri_escape($th{$_}) }
keys %th;
printf(
'<a href="%s">%s</a>',
encode_entities($url),
encode_entities($th{name}),
);
That is, URI escaping (which deals with things like space being encoded as %20) needs to be done to each component of the URI, but not the URI as a whole. And HTML entity encoding (which deals with things like "&" becoming "&") needs to be done to all strings used in the HTML.
That assumes you're building your HTML using string processing, which is what many people do. If you're instead building it using a DOM library (e.g. XML::LibXML, HTML::HTML5::Builder, etc) then the library should take care of the HTML entity encoding, but it won't attempt URI escaping. | [reply] [d/l] [select] |
|
Re: Joining an array
by ww (Archbishop) on Feb 11, 2012 at 01:47 UTC
|
So, "why?" /me said to myself, "Why can't Perl chew on this?"
my $i = 0; my $str = "$arr[$i]=$arr[++$i]" x($#arr/2);
or this?
my $i = 0; my $str = "$arr[$i]=$arr[++$i]" x($#arr/2) . "&";
Now, of course the first snippet left the question of inserting the ampersands un-resolved, but that's OK, because Perl wants an int after the x operator and 5/2 isn't likely to ever be an int. Then, the second suffers the same defect ... and inserts the ampersand only AFTER printing the second name=John; a 'gotcha' for this nutty idea that I don't see a way around (in this approach).
But, the idea perked on, leading to this, just because TIMTOWTDI:
#!/usr/bin/perl
use strict;
use warnings;
use 5.014;
# desired: name=John&number=7&status=unknown
my @arr = qw(name John number 7 status unknown);
my $i = 0;
my $str = "$arr[$i]=$arr[++$i]";
$str .= "&$arr[++$i]=$arr[++$i]";
$str .= "&" . "$arr[++$i]=$arr[++$i]";
say $str;
Output? As specified. | [reply] [d/l] [select] |
|
Thanks ww. I should have mentioned though that there are a variable number of elements in the array.
| [reply] |
|
Variable number of elements is not a problem; it's just a circumstance that demands a little more code....
So, let's see... . o O
We know how to count the elements, $#arr... so that means we'll need to repeat the next-to-last line $#arr/2 times.
Oops. Not an integer. Well, we knew that already too, but after we've printed the first pair, we should need to repeat either of the next two lines ( ($#arr/2) -1 ) times. Or we could start with ( ($#arr +1)/2 ). Either way, Shazam! The problem of an unknown number of pairs in the array is (almost) solved, except for the trivial act of actually writing the code.
Do you smell a loop coming? I do.
Again, this is a case of TIMTOWTDI that's NOT worth pursuing, except for the mental exercise (or maybe, an obfuscation contest). The solutions with a module that's designed to do just what you want are clearly the way to go in the real world.
It does, however, have a smidgen of value for readers here in the Monastery: it emphasizes the need for precision and completeness in problem statements.
| [reply] [d/l] [select] |
|
Re: Joining an array
by johngg (Canon) on Feb 11, 2012 at 13:37 UTC
|
Another splice method with two joins, one in a map, and an on-the-fly subroutine.
knoppix@Microknoppix:~$ perl -E '
> @arr = qw{
> name
> John
> number
> 7
> status
> unknown
> };
> $str =
> join q{&},
> map { join q{=}, @$_ }
> sub {
> push @ret, [ splice @_, 0, 2 ] while @_;
> return @ret;
> }->( @arr );
> say $str;'
name=John&number=7&status=unknown
knoppix@Microknoppix:~$
| [reply] [d/l] |
Re: Joining an array
by JavaFan (Canon) on Feb 11, 2012 at 17:24 UTC
|
sub FETCH {${$_[0]}++%2?"=":"&"}
sub TIESCALAR {bless\my$i}
tie $", "main";
my @array = qw[name John number 7 status unknown];
say "@array";
__END__
name=John&number=7&status=unknown
| [reply] [d/l] |
|
How this code works? $" is the separator used when arrays are extrapolated in certain contexts (like in say "@array"), tie-ing it to 'main' means that it will be implemented in the scope of the main program. I'm not sure about bless\my$i and what ${$_[0]} is referring to when invoked in FETCH.
Give a man a fish and you feed him for a day. Give him a fishnet and you feed him for a lifetime. Ooops! Wait a minute! What if the fishnet brakes down? He will ask for an other fishnet or worse starve to death. Better yet to teach him how to make a fishnet. But what if he forgets how to make a fishnet? It is better to teach him where to look for knowledge on how to make a fishnet. But this can also go wrong. So ultimately it is best just telling him: 'Help yourself, stupid!'
| [reply] [d/l] [select] |
|
When you tie a variable, the TIE* method, in this case TIESCALAR, has to return an object. An object is nothing more than a blessed reference. \my $i is a reference to the variable $i. bless blesses its argument. If bless is given just one argument, it blesses the reference to the current package.
The first argument to the FETCH method is the object returned from bless. $_[0] is the first argument of a method, and ${EXPRESSION THAT RETURNS A SCALAR REFERENCE} is a way to dereference a scalar reference. So, you get back the $i.
| [reply] [d/l] [select] |
|
|
tie, perltie
Update: What happens is that the main package is (ab)used as a class implementing a tied scalar for $". Whenever perl wants to use the value of the tied scalar $", it calls the object method FETCH instead, in this case, main::FETCH. And that method returns either "=" or "&", depending on how often it was called before. The modulo operator % switches between the two values with every call. To construct the object on which FETCH will be invoked, tie calls the TIESCALAR class method that returns a blessed reference to a scalar value, that value is used to count the calls inside FETCH.
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
| [reply] [d/l] [select] |
Re: Joining an array
by trizen (Hermit) on Feb 11, 2012 at 11:36 UTC
|
Here is a splice + join version...
my @t = qw(name John number 7 status unknown);
my $i = -1;
foreach my $x (1 .. $#t) {
splice(@t, $i += 2, 0, $x % 2 ? '=' : '&');
}
print join '', @t;
| [reply] [d/l] |
|
my @array = qw(name John number 7 status unknown);
my $i = -2;
my $x = $#array - 1;
my $str = '';
while (1) {
if ($i + 3 < $x) {
$str .= $array[$i += 2] . '=' . $array[$i + 1] . '&';
}
else {
$str .=
$i + 3 == $x ? $array[-3] . '=' . $array[-2] . '&' . $arra
+y[-1]
: $i + 2 == $x ? $array[-2] . '=' . $array[-1]
: $array[-1];
last;
}
}
print "$str\n";
| [reply] [d/l] |
Re: Joining an array
by nemesdani (Friar) on Feb 11, 2012 at 20:09 UTC
|
An idea, if you construct the array in the first place: Maybe you should consider using a hash instead of an array. Makes this problem much more easy. | [reply] |
|
Yes I am using a hash at the moment and it works fine. But as my dataset has grown performance has become an issue, and I notice I am constantly looping over hashes and arrays.
EDIT: looking again I see am using both a hash and an array - the array purely to keep the order.
| [reply] |
|
The OP said he wants to preserver order. How do you suggest to do that using a hash, which considering the efficiency request from the OP?
| [reply] |
Re: Joining an array
by Anonymous Monk on Feb 11, 2012 at 07:40 UTC
|
#!/usr/bin/perl --
use strict; use warnings;
use CGI; use URI;
print join "\n",
CGI->new( { @$_ } )->query_string,
Yo( @$_ ),
Yuri( @$_ ),
, "\n"
foreach
[qw( name John number 7 status unknown )],
[qw( a >&< q ];[ arf =&= )];
exit 0;
sub Yuri {
use URI;
my $u = URI->new;
$u->query_form( @_ );
$u->query ;
}
sub Yo {
use CGI;
#~ use CGI qw/ -oldstyle_urls /;
local $CGI::USE_PARAM_SEMICOLONS=0;
my $q = CGI->new;
for( my $i = 0; $i < @_; $i +=2 ){
$q->param( $_[$i], $_[$i+1] );
}
$q->query_string;
}
__END__
number=7;status=unknown;name=John
name=John&number=7&status=unknown
name=John&number=7&status=unknown
a=%3E%26%3C;q=%5D%3B%5B;arf=%3D%26%3D
a=%3E%26%3C&q=%5D%3B%5B&arf=%3D%26%3D
a=%3E%26%3C&q=%5D%3B%5B&arf=%3D%26%3D
| [reply] [d/l] |
Re: Joining an array
by chessgui (Scribe) on Feb 11, 2012 at 07:42 UTC
|
Here is a one liner without any module:
@array=qw(name John number 7 status unknown);
print join('',(map {!($_%2)?"$array[$_]=".$array[$_+1]:$_<$#array?'&':
+'';}(0 .. $#array)));
| [reply] [d/l] |
|
@a=qw(name John number 7 status unknown);
print join('',(map {$a[$_].($_%2?'&':'=');}(0 .. $#a-1))).$a[$#a];
Give a man a fish and you feed him for a day. Give him a fishnet and you feed him for a lifetime. Ooops! Wait a minute! What if the fishnet brakes down? He will ask for an other fishnet or worse starve to death. Better yet to teach him how to make a fishnet. But what if he forgets how to make a fishnet? Better yet to teach him where to look for knowledge on how to make a fishnet. But this can also go wrong. So ultimately it is best just telling him: help yourself. If you want to make it sure add 'stupid' and an exclamation mark, like this: 'Help yourself, stupid!'
| [reply] [d/l] |
Re: Joining an array
by Anonymous Monk on Feb 11, 2012 at 14:05 UTC
|
#!/usr/bin/perl --
use strict;
use warnings;
local $\ = $/;
my @f = qw(name John number 7 status unknown);
print do {
my $i = -2;
join q/&/, map { $i += 2; join q/=/, $f[$i], $f[ $i + 1 ] } 0 ..(
+$#f / 2 );
};
print join q/&/, map { $_ % 2 ? () : join q/=/, $f[$_], $f[ $_ + 1 ] }
+ 0 .. $#f;
print join q/&/,
map {
$_ % 2
? ()
: join q/=/, $f[$_], $f[ $_ + 1 ]
} 0 .. $#f;
__END__
name=John&number=7&status=unknown
name=John&number=7&status=unknown
name=John&number=7&status=unknown
| [reply] [d/l] |