在Enumerable模块中理解grep



我非常感谢任何帮助我的第一个堆栈溢出的帖子!

我通常感到困惑,为什么我每次尝试运行代码时都会收到空数组。我正在创建一个方法,将过滤性别"M"并返回其元素

我确信有很多方法可以成功运行这段代码,但我对使用grep及其功能很感兴趣。这是一条捷径,我认为它值得学习。再次感谢您。

students = [
{name: 'John', grade: 8, gender: 'M'},
{name: 'Sarah', grade: 12, gender: 'F'},
{name: 'Bob', grade: 16, gender: 'M'},
{name: 'Johnny', grade: 2, gender: 'M'},
{name: 'Ethan', grade: 4, gender: 'M'},
{name: 'Paula', grade: 8, gender: 'F'},
{name: 'Donald', grade: 5, gender: 'M'},
{name: 'Jennifer', grade: 13, gender: 'F'},
{name: 'Courtney', grade: 15, gender: 'F'},
{name: 'Jane', grade: 9, gender: 'F'}
]
def is_male(gender)
gender.grep(/M/) { |gend| gend[:gender] }
end
p is_male(students)

FromEnumerable#grep的文档:

grep(pattern) → array
grep(pattern) { |obj| block } → array

返回enumPattern === element对应的所有元素的数组。如果提供了可选块,则将每个匹配的元素传递给它,并将块的结果存储在输出数组中。

重要的部分是该方法返回计算Pattern === elementtrue的元素。但是/M/ === {name: 'John', grade: 8, gender: 'M'}不会返回true,对于数组中的所有其他元素也是如此。

因此你的结果集一开始是空的。

块-在您的示例中{ |gend| gend[:gender] }-仅在模式匹配时和之后计算。该块改变了整个调用的返回值,但不改变模式匹配的完成方式。

请注意Rexgxp#===在这种情况下的文档。

我正在创建一个方法,将过滤性别"M"并返回其元素

考虑到上述要求,你的命名似乎很误导人:

def is_male(gender)
# ...
end

上面的代码看起来像一个获取性别并检查其是否为男性的方法。我希望是这样的:

is_male('M') #=> true
is_male('F') #=> false

is_male(students)也不清楚-它检查是否有一个男学生在给定的数组?或者如果所有的学生都是男性?不管怎样,这听起来都不像过滤。


让我们从重命名方法和它的参数开始,以更紧密地匹配您的需求:

def male_students(students)
# ....
end

如果你想使用grep,你必须提供一个模式,这是一个对象响应===。正则表达式不起作用,因为它对字符串进行操作,而我们的学生是哈希(后面会详细介绍)。您可以使用Proc来代替,它也响应===:

def male_students(students)
is_male = ->(student) { student[:gender] == 'M' }
students.grep(is_male)
end

但是使用select:

更容易
def male_students(students)
students.select { |student| student[:gender] == 'M' }
end

另一个选择是为你的学生使用自定义类,因为现在,你的学生只是散列:

class Student
attr_accessor :name, :grade, :gender
def initialize(name:, grade:, gender:)
@name = name
@grade = grade
@gender = gender
end
def male?
gender == 'M'
end
def female?
gender == 'F'
end
end

现在,相应地改变数组:

students = [
Student.new(name: 'John', grade: 8, gender: 'M'),
Student.new(name: 'Sarah', grade: 12, gender: 'F'),
Student.new(name: 'Bob', grade: 16, gender: 'M'),
Student.new(name: 'Johnny', grade: 2, gender: 'M'),
Student.new(name: 'Ethan', grade: 4, gender: 'M'),
Student.new(name: 'Paula', grade: 8, gender: 'F'),
Student.new(name: 'Donald', grade: 5, gender: 'M'),
Student.new(name: 'Jennifer', grade: 13, gender: 'F'),
Student.new(name: 'Courtney', grade: 15, gender: 'F'),
Student.new(name: 'Jane', grade: 9, gender: 'F'),
]

你可以使用这个非常简单的语法:

students.select(&:male?)
#=>
# [
#   #<Student:0x00007fb18d826ce8 @name="John", @grade=8, @gender="M">,
#   #<Student:0x00007fb18d826b58 @name="Bob", @grade=16, @gender="M">,
#   #<Student:0x00007fb18d826a90 @name="Johnny", @grade=2, @gender="M">,
#   #<Student:0x00007fb18d8269c8 @name="Ethan", @grade=4, @gender="M">,
#   #<Student:0x00007fb18d826838 @name="Donald", @grade=5, @gender="M">
#  ]

Array/Enumerable中任何其他有用的方法:

students.any?(&:male?)  #=> true
students.all?(&:male?)  #=> false
students.count(&:male?) #=> 5

最新更新