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!

Find array index

Status
Not open for further replies.

threepwood

Programmer
Aug 24, 2007
8
ES
Hi, I would like to take the index from an array, if I know the value.

E.g. I have the array:
@var = (a,b,c,d,e);

and I would like to know the position of the element 'c' into the array (i.e. 3). Is there any way to get this one with 'grep' or another function?

Salu2 and thanks in advance,

Threepwood.
 
Have you already written some code, because I can think of a few ways to do this, and I was wondering which approach you took
 
I used this...

Code:
#! usr/bin/perl
use strict;
print "Content-type: text/html\n\n";
my ($tofind, $no, @var);
$tofind = 'c';
$no = '0';
@var = ('a', 'b', 'c', 'd', 'e');
do {
if (@var[$no] eq $tofind) {
$no = $no + 1;
print "$tofind is at position $no";
}
$no = $no + 1;
} until ($no == 4);

Although, its a longwinded solution and there are better methods
 
Thanks, I have tested the code from the brigmar's link, and it naturally works:
my( $found, $index ) = ( undef, -1 );
for( $i = 0; $i < @array; $i++ )
{
if( $array[$i] =~ /Perl/ )
{
$found = $array[$i];
$index = $i;
last;
}
}

But I thoughted that there was a way similar to the function 'first' in the same link ('with less lines').
 
I changed my version of the code slightly so that the only variable that the script needs is which letter/word you want to find (because before it sort of needed to know how many letters/words the array contained (shown by the until ($no == 4);)...

Code:
#! usr/bin/perl
use strict;
#####
print "Content-type: text/html\n\n";
#####
my ($tofind2, $no2, $found, @var2);
$tofind2 = 'e';
$no2 = '0';
@var2 = ('a', 'b', 'c', 'd', 'e');
do {
if (@var2[$no2] eq $tofind2) {
$no2 = $no2 + 1;
print "<p><font face=arial size=2><b>$tofind2 is at position $no2</b></font>";
$found = '1';
}
$no2 = $no2 + 1;
} until ($found == '1');
 
If your array is fairly static (doesn't change too often) and the operation of finding the index is frequent, you can try this:
Code:
$i=0;
map{$index{$_}=$i++}@array;
[tt]$index{c}[/tt] is now 2 (not 3 of course) in your example.

prex1
: Online tools for structural design
: Magnetic brakes for fun rides
: Air bearing pads
 
Sorry, could you explain the code. I am a little novice in this kind of code :-(

I have tried this one, but it doesn't work:

Code:
$i=0;
$a="c";
@array = (a,b,c,d,e,f);
print map{$index{$a}=$i++}@array;

I have a static array and I need call to my subrutine many, many times:

Code:
my ($before, $after) = getBeforeAndAfterGenes ($id, @positions);

sub getBeforeAndAfterGenes () {
  my ($id, @p) = @_;
  my ($after, $before);
  
  for (my $x = 0; $x < @p; $x++) {
    if ($p[$x] =~ /^$id/) {
      $after = $p[$x-1];
      $before = $p[$x+1];
      last;
    } 
  }   
    
  return $after, $before;
}
 
post the real data you are working with and what you are searching for: patterns or strings or numbers.

------------------------------------------
- Kevin, perl coder unexceptional! [wiggle]
 
Well the array contains a list of gene identifiers, ordered by the position in the chromosome (similar to a line):
@positions = (P122343,Q281170,X726001,...);

And $id is one of them. I have a list of genes and I want to know what are both the previous and the following genes to each one.

The list of genes can have 30000 or more genes. And so I need a efficient algorithm, because it works with my version but it is slow.
 
If you are calling the subroutine many times, might be better to create a hash first.


Code:
[url=http://perldoc.perl.org/functions/use.html][black][b]use[/b][/black][/url] [green]strict[/green][red];[/red]
[black][b]use[/b][/black] [green]warnings[/green][red];[/red]
[url=http://perldoc.perl.org/functions/my.html][black][b]my[/b][/black][/url] [blue]@positions[/blue] = [red]qw([/red][purple]P122343 Q281170 X726001 C122343 R281170 H726001[/purple][red])[/red][red];[/red]
[black][b]my[/b][/black] [blue]%hashOfPositions[/blue] = [red]([/red][red])[/red][red];[/red]
[olive][b]for[/b][/olive] [black][b]my[/b][/black] [blue]$i[/blue] [red]([/red][fuchsia]0[/fuchsia] .. [blue]$#positions[/blue][red])[/red] [red]{[/red]
   [blue]@[/blue][red]{[/red][blue]$hashOfPositions[/blue][red]{[/red][blue]$positions[/blue][red][[/red][blue]$i[/blue][red]][/red][red]}[/red][red]}[/red] = [red]([/red][blue]$positions[/blue][red][[/red][blue]$i[/blue]-[fuchsia]1[/fuchsia][red]][/red],[blue]$positions[/blue][red][[/red][blue]$i[/blue]+[fuchsia]1[/fuchsia][red]][/red][red])[/red][red];[/red]
[red]}[/red]
[black][b]my[/b][/black] [blue]$id[/blue] = [red]'[/red][purple]X726001[/purple][red]'[/red][red];[/red]
[black][b]my[/b][/black] [red]([/red][blue]$before[/blue], [blue]$after[/blue][red])[/red] = [maroon]getBeforeAndAfterGenes[/maroon] [red]([/red][blue]$id[/blue],\[blue]%hashOfPositions[/blue][red])[/red][red];[/red]
[url=http://perldoc.perl.org/functions/print.html][black][b]print[/b][/black][/url] [red]"[/red][purple][blue]$before[/blue], [blue]$after[/blue][/purple][red]"[/red][red];[/red]
[url=http://perldoc.perl.org/functions/sub.html][black][b]sub[/b][/black][/url] [maroon]getBeforeAndAfterGenes[/maroon] [red]{[/red]
   [black][b]my[/b][/black] [red]([/red][blue]$id[/blue],[blue]$href[/blue][red])[/red] = [blue]@_[/blue][red];[/red]
   [black][b]my[/b][/black] [blue]$before[/blue] = [blue]$href[/blue]->[red]{[/red][blue]$id[/blue][red]}[/red][red][[/red][fuchsia]0[/fuchsia][red]][/red] || [red]'[/red][purple]none[/purple][red]'[/red][red];[/red]
   [black][b]my[/b][/black] [blue]$after[/blue]  = [blue]$href[/blue]->[red]{[/red][blue]$id[/blue][red]}[/red][red][[/red][fuchsia]1[/fuchsia][red]][/red] || [red]'[/red][purple]none[/purple][red]'[/red][red];[/red]
   [url=http://perldoc.perl.org/functions/return.html][black][b]return[/b][/black][/url][red]([/red] [blue]$before[/blue], [blue]$after[/blue][red])[/red][red];[/red]
[red]}[/red]
[tt]------------------------------------------------------------
Pragmas (perl 5.8.8) used :
[ul]
[li]strict - Perl pragma to restrict unsafe constructs[/li]
[li]warnings - Perl pragma to control optional warnings[/li]
[/ul]
[/tt]

------------------------------------------
- Kevin, perl coder unexceptional! [wiggle]
 
My chromosome size could grow very much, with new ones, and so it would take a lot of memory. But it seems as a nice alternative. Thanks!

Please somebody could explain the 'map' alternative?

I will now try your code, Kevin.

Salu2,

Threepwood.
 
map() is just a function that loops through a list/array. It is very similar to a for/foreach loop but not exactly the same.

------------------------------------------
- Kevin, perl coder unexceptional! [wiggle]
 
The map is creating an index by using a hash... effectively a reverse lookup array.
 
To save memory space the best is the following:
Code:
$i=0;
map{$index{$_}=$i++}@array;
$position=$index{id};
$before=$array[$position-1]if$position;
$after=$array[$position+1]if$position<$#array;
Be aware however that this method of using a hash doesn't work if there are repetitions in the gene list: however don't think this is the case as you would have to decide on which occurrence to make the search.
As a side note you should also decide what to do when [tt]$position[/tt] is first or last in [tt]@array[/tt]

prex1
: Online tools for structural design
: Magnetic brakes for fun rides
: Air bearing pads
 
Thanks, it is a nice code. I will compare both of them, and I will try to understand the map function.

My array contains the first element in the last position too, and the last element in the first position. So I have something similar to a circle.

Thanks again and salu2,

Threepwood.
 
I have just realize that mi circle chromosome will have 2 repeated element in the ends. Will it work? If it doesn't work, I will include a sub-name to these 2 elements.
 
I think the best would be to include two extra elements as the start and end elements, instead of repeating the first and last elements (that the hash won't catch, as a hash has unique element names). I mean the following:
Code:
@array=('a','b','c','d','e');
$index{start}=0;
$index{end}=1+$#array;
$i=1;
map{$index{$_}=$i++}@array;
$position=$index{id};
$before=$array[$position];
$after=$array[$position+2];
With this you'll get [tt]$before eq'start'[/tt] (or whatever id you prefer, but not one of the id's in the list) when you look for the first [tt]id[/tt] in the sequence (and similarly for the last one).
Notice that it is no more necessary to test the value of $position every time, with a noticeable better performance.
Also, if you are not 100% sure that [tt]id[/tt] will be in your list when you do [tt]$position=$index{id};[/tt] you should replace this with [tt]$position=$index{id}if exists$index{id};[/tt] (with a small loss in performance), as otherwise a new element with value [tt]undef[/tt] would be added to the hash.
Last note: [tt]map{$index{$_}=$i++}@array;[/tt] is in this case exactly equivalent to [tt]for(@array){$index{$_}=$i++}[/tt], the only difference being that [tt]map[/tt] returns a list (not used here). I would also expect a somewhat better performance from [tt]map[/tt], but am not sure of that.

prex1
: Online tools for structural design
: Magnetic brakes for fun rides
: Air bearing pads
 
I have solved the problem with 2 arrays. @p_prov contain the positions for one only chromosome, and %p has the before and after genes for all chromosomes.

Then, I add the first gene ($p_prov[0]) to the last element. But the first element has already the right before and after genes, because:
$p_prov[-1] = $p_prov[$#p_prov];

(I didn't know this rule).

Code:
for my $x (0 .. $#p_prov) {
  @{$p{$p_prov[$x]}} = ($p_prov[$x-1], $p_prov[$x+1]);
}

# Last gene is joined to first one (circular chromosome)
# First gene is already joined to the last one (-1 element in the array)
@{$p{$p_prov[$#p_prov]}}[1] = $p_prov[0];
 
If you need to treat the list as a true circular list, just do the following:
Code:
$i=0;
map{$index{$_}=$i++}@array;
$position=$index{id};
if($position>0){
  $before=$array[$position-1];
  if($position<$#array){
    $after=$array[$position+1];
  }else{
    $after=$array[0];
  }
}else{
  $before=$array[$#array];
}
Your code is not the best in memory usage and performance, but if it suits your needs...


prex1
: Online tools for structural design
: Magnetic brakes for fun rides
: Air bearing pads
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top