vaindil
vaindil

Reputation: 7846

Using grep in Perl

I have the below code snippet. I need to insert a grep command in the appropriate place to find only grades between 80 and 89 and store them in a separate array called @GradeB. I've tried using @GradeB = grep("8[0-9]", @Grades);, but that puts everything into @GradeB. Where am I going wrong to get only grades between 80-89 into @GradeB? For full disclosure, this is is homework, but I am allowed to use any and all resources available to me.

@Grades = ("Name: Shemp      Grade: 82",
           "Name: Curly      Grade: 62",
           "Name: Curly Joe  Grade: 58",
           "Name: Joe        Grade: 50",
           "Name: Moe        Grade: 88",
           "Name: Larry      Grade: 82");


# grep command here
print join("\n", @GradeB);

Upvotes: 0

Views: 575

Answers (4)

ThisSuitIsBlackNot
ThisSuitIsBlackNot

Reputation: 24063

grep returns each item in a list for which the expression evaluates to true. The expression

"8[0-9]"

is a string, and it always evaluates to true. You probably meant to use a regular expression instead:

my @b_grades = grep /Grade: 8[0-9]$/, @grades;
# Alternately, my @b_grades = grep { /Grade: 8[0-9]$/ } @grades;

Note that I've used a more specific regex than just /8[0-9]/, which would match things like:

Name: Inmate 12789132    Grade: 12

This approach works, but isn't very flexible. As Borodin points out in the comments, changing the regex to match scores between, say, 79 and 88 is difficult. It would be better to extract the scores so we can do numerical comparisons:

my @b_grades = grep {
                        if (/Grade: (\d+)$/)  {
                            $1 if ($1 >= 80 && $1 < 90);
                        }
                    } @grades;

This is just a different way of writing Borodin's solution.


Note that an array is not the best choice of data structure for this. When you have a list of key/value pairs, think "hash." In this case, you can use names as keys and grades as values:

my %grades = (
    Shemp       => 82,
    Curly       => 62,
    'Curly Joe' => 58,
    Joe         => 50,
    Moe         => 88,
    Larry       => 82
);

Note that quotes are optional around keys that only consist of letters, digits, and underscores. To access the values in the hash, use the aptly-named values function:

my @grades = values %grades;

Using grep as before to get all the B's:

my @b_grades = grep { $_ >= 80 && $_ < 90 } values %grades;
print join ',', @b_grades;
# 88,82,82

This gives us a list of grades, but now we have no idea who they belong to. For that, we need to use keys, which, strangely enough, returns a list of all the keys in our hash:

my @b_students = grep { $grades{$_} >= 80 && $grades{$_} < 90 } keys %grades;
print "Name: $_\tGrade: $grades{$_}\n" for @b_students;
# Name: Moe       Grade: 88
# Name: Larry     Grade: 82
# Name: Shemp     Grade: 82

Upvotes: 1

Borodin
Borodin

Reputation: 126722

It is best not to use capitals for lexical variable names.

This solution works by extracting the string of digits that follow Grade: and comparing it with the specified range.

use strict;
use warnings;

my @grades = (
  "Name: Shemp      Grade: 82",
  "Name: Curly      Grade: 62",
  "Name: Curly Joe  Grade: 58",
  "Name: Joe        Grade: 50",
  "Name: Moe        Grade: 88",
  "Name: Larry      Grade: 82",
);

print "$_\n" for grep {
  /grade\s*:\s*(\d+)/i and $1 >= 80 and $1 < 90;
} @grades;

output

Name: Shemp      Grade: 82
Name: Moe        Grade: 88
Name: Larry      Grade: 82

Upvotes: 2

Take look at perldoc for grep:

   grep BLOCK LIST
   grep EXPR,LIST

Grep evaluates BLOCK or EXPR for every element of LIST and returns list composed of elements for which BLOCK or EXPR evaluate to true value. As you specify string "8[0-9]", which is always true, every list element is returned.

You need to pass regular expression as EXPR:

@GradeB = grep /8[0-9]/, @Grades;

Upvotes: 2

Birei
Birei

Reputation: 36262

You can use a combination of map() and grep(). The first one creates an arrayref to keep the whole line and the last number in it, the grep() compares them, and last map() extracts the whole line of those which matched:

#!/usr/bin/env perl

use warnings;
use strict;

my @Grades = ("Name: Shemp      Grade: 82",
           "Name: Curly      Grade: 62",
           "Name: Curly Joe  Grade: 58",
           "Name: Joe        Grade: 50",
           "Name: Moe        Grade: 88",
           "Name: Larry      Grade: 82");


# grep command here

my @GradeB = 
    map { $_->[0] } 
    grep { $_->[1] >= 80 and $_->[1] <= 89 } 
    map { [$_, (split)[-1]] } @Grades;

print join("\n", @GradeB);

It yields:

Name: Shemp      Grade: 82
Name: Moe        Grade: 88
Name: Larry      Grade: 82

Upvotes: 1

Related Questions