Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Quick 'splice();' question. 2

Status
Not open for further replies.

1DMF

Programmer
Jan 18, 2005
8,795
GB
hi,

I have an array I want to remove elements at different indexes, relative to the value of a key held in the hash.

i've seen this on other posts
my $index = 0;
while ($index <= $#items ) {
my $value = $items[$index];
print "testing $value\n";
if ( $value == 1 or $value == 3 ) {
print "removed value $value\n";
splice @items, $index, 1;
} else {
$index++;
}
}
it seems they are using array, index(offset), length

does this have to be specified can I imply current location as follows.
Code:
for(@array){
  if($_->{'key'} eq "value"){splice(@array);}
}

Just thought I'd check before I ploughed ahead.

Thanks.
1DMF


"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
You have to specify the index, yes.

For what you're intending in the second code example using splice:
Code:
for(0 .. $#array){
  if($array[$_]_->{'key'} eq "value"){splice(@array, $_, 1);}
}

However, to remove elements like that, I'd probably use grep rather than splice:
Code:
@array = grep { $_->{key} ne 'value' } @array;
I've changed to 'eq' to 'ne' because with the grep statement, you specify which ones you want to keep (those thatn aren't equal to "value") rather than the ones you want to remove.
 
wow - as always ishnid, your a gent.

not sure grep is what I want because there will actually be a few if's then splice, i'd cut the code down just for the example.

unless i used $_->{key} ne 'value' && $_->{key} ne 'value' , etc... however I want to create a separate hash with the current arrays index when my criteria is matched.

so the full code would be ....
Code:
for(0 .. $#array){
  if($array[$_]_->{'key'} eq "value1"){
   %hash1 = $array[$_];
   splice(@array, $_, 1);
  }
  if($array[$_]_->{'key'} eq "value2"){
   %hash2 = $array[$_];
   splice(@array, $_, 1);
  }
}

however i have a concern, if lets say the first match is at index 3, so the splice command activates, now what was in index 4 is in index 3, but the for loop goes round and now $_ equals 4 and is checking index 4 and so has skipped an item as it was "shifted" to index 3.

Is that right?


"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
You're right - I've forgotten one of the basics of splicing. Reverse the order so that you're processing from finish to start. That way, any elements you remove won't affect elements you've yet to process. i.e.:
Code:
for( reverse 0 .. $#array){
  if($array[$_]_->{'key'} eq "value1"){
   %hash1 = $array[$_];
   splice(@array, $_, 1);
  }
  if($array[$_]_->{'key'} eq "value2"){
   %hash2 = $array[$_];
   splice(@array, $_, 1);
  }
}
 
thanks ishnid,

one last question, (reverse 0 .. $#array) is that the PERL way of saying for x=10 to 0 step -1

"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
Yup. Explanation for anyone who's interested:

0 .. $#array creates a list of ints from 0 up to the last index number that's present in $#array. That for loop would be equivalent to the C-style:
Code:
for ( my $i = 0; $i < @array; $i++ ) {
   # something
}
The reverse function reverses a list, so if your list is 0, 1, 2, 3, 4, 5 it will become 5, 4, 3, 2, 1, 0. In a C-style loop, it's now:
Code:
for ( my $i = $#array; $i >= 0; $i-- ) {
   # something
}
 
thanks ishnid.

however, I really could do with your help on another closely related issue.

Ok I now have my data sorted how I want it like so
Code:
for(reverse 0 .. $#messages){
  
  if($messages[$_]->{'MessID'} eq "Welcome"){
    %welcome = $messages[$_];       
    splice(@messages, $_, 1);
    }        
  if($messages[$_]->{'MessID'} eq "Contacts"){
    %contacts = $messages[$_];
    splice(@messages, $_, 1);
  }
  if($messages[$_]->{'MessID'} eq "Scroller"){
    %scroller = $messages[$_];
    splice(@messages, $_, 1);
  }
    
}

I think i've correctly created the template references like so
Code:
  $template->param( 'Welcome' => \%welcome );
$template->param( 'Contacts' => \%contacts );
$template->param( 'Scroller' => \%scroller );
$template->param( 'Updates' => \@messages );

great, but I cannot see while scouring CPAN re HTML::Template module how you talk to the hash via the template.

I've tried using <tmpl_loop name='Welcome'><tmpl_var name='Message'></tmpl_loop>

But that errors because 'Welcome' is a hash not an array.

And if I just put <tmpl_var name='Welcome'> all I get is HASH(0x1aca0d4) - the hash reference.

How do I get to Welcome->{'Message'}



"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
if($messages[$_]->{'MessID'} eq "Welcome"){
%welcome = $messages[$_];

I have a doubt in this statement.
Arent you assigning a reference to a hash to a hash.

Shouldnt it be
$welcome = $message[$_];

and
$template->param( 'Welcome' => $welcome );

I think this might work.
 
ok so you now give a scalar the reference to the hash and I assume $template->param( 'Welcome' => $welcome ); creates a template variable with the hash reference.

how do I get to $welcome->{'Message'}, $welcome->{'MessID'} , $welcome->{'MessDate'}




"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 

Doesnt it work when u try this

<tmpl_loop name='Welcome'>
<tmpl_var name='Message'>
<tmpl_var name='MessId'>
<tmpl_var name='MessDate'>
</tmpl_loop>
 
I think this mite work

$template->param( 'Welcome' => [$welcome] );

Basically you will hav to pass the loop variable an array reference of hash references.

So if you just want to pass one hash reference you can do the above.
Or you can try this if you want to pass many references

# Repeat this statement for all the hash references "$welcome"
push(@refereces,$welcome);


and finally
$template->param( 'Welcome' => \@references );

And the template will be like this

<tmpl_loop name='Welcome'>
<tmpl_var name='Message'>
<tmpl_var name='MessId'>
<tmpl_var name='MessDate'>
</tmpl_loop>


Assuming all the hash references have Message, MessID and MessDate keys
 
yeah I guessed in the end even though I only have one hash, you still need to stick it in an array and then use <tmpl_loop>, shame really would be cool if you could use

<tmpl_var name='Welcome->{'Message'}'> or such like and only needed to create a template var passing it the hash reference.

I opted for this in the end for the single hashes
Code:
...the creation bit within the for loop....
$welcome = $message[$_];
.... placing in the template params ....
$template->param( 'Welcome' => $welcome->{'Message'} );

Thanks for your input.

"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
Hmm. If you're only using one value, why put it in a tmpl_loop at all? You could just do:
Code:
<!-- tmpl_var name="Message" -->
<!-- tmpl_var name="MessId" -->
<!-- tmpl_var name="MessDate" -->
and then:
Code:
$tmpl->param( %welcome );
 
yup, that's what I opted for
Code:
$template->param( 'Welcome' => $welcome->{'Message'} );

for the welcome and contacts message I only want the actual message not any other key/value.

basically I have a Messages table, but it holds the welcome message,updates,contact message and scroller messages.

there are many messages with "Update" and "Scroller" as the MessID but only 1 for the "Welcome" and "Contacts".

I was doing the following...
Code:
my @welcome = &getSQL("Messages","MessID,Message","MessID='Welcome'");
my @updates = &getSQL("Messages","MessID,MessDate,Message","MessID='Updates'","MessDate DESC, AutoID DESC");
my @contacts = &getSQL("Messages","MessID,Message","MessID='Contacts'");
# Scroller Message
my @scroller = &getSQL("Messages","MessID,Message","MessID='Scroller'");

And don't forget originaly the HTML was in the PERL, so I then built the HTML as I went along.

However just look at all those SQL DB request, talk about OTT and slowing things down, so I was looking to only do one SQL request getting all messages and then using PERL to sort them as needed.

Hence your fantastic help with the splice command, TYVM.

I was just hoping there was a way of creating a template var that was still a hash, so was referencable via the template using normal hash syntax.

But it would seem this is not the case, seemed silly having to create an array and then loop it to use in the way I was thinking, when the array would only ever have one index which was basicaly a hash/record.

I use arrays and hashes as a record set mechanism, I take it the template requires you to create an array to hold each hash to use in this mannor even if there will only ever be one record.

Would be a neat feature don't you think if you could pass a hash to the template and still treat it like a hash, rather than individual scalars.




"In complete darkness we are all the same, only our knowledge and wisdom separates us, don't let your eyes deceive you.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top