-1

I've tried different ways and this is probably the closest that I got to it. I am trying to write a method that takes in an array of strings and returns it containing the strings that are at least 5 characters long and end with "y".

I'm a beginner and this is my second problem I've come across with, and I've tried multiple if statements and using a while loop, however I could not get to it and now this is where I am at. Thank you!

def phrases(arr1, arr2)
  arr1 = ["funny", "tidy", "fish", "foogiliously"]
  arr2 = ["happily", "lovely", "hello", "multivitaminly"]

  if (arr1.length > 5 && arr1.length == "y")
    return arr1
  elsif (arr2.length > 5 && arr2.length == "y")
    return arr2
  end
end

puts phrases(["funny", "tidy", "fish", "foogiliously"])
puts phrases(["happily", "lovely", "hello", "multivitaminly"])
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
samibacon
  • 3
  • 1
  • 2
  • Your method takes in two arguments, and then immediately overrides those with hard-coded arrays? You then call your method with only one argument. Furthermore, `Array#length` returns an `Integer`, so `arr1.length > 5` is valid, but `arr1.length == 'y' is not. – magni- Jul 23 '19 at 01:30
  • You should use a regex to do matching. – Beartech Jul 23 '19 at 01:31
  • Ohh okay, no wonder. @magni – samibacon Jul 23 '19 at 01:53
  • You will agree that the string "quickly", because it has 7 letters and ends in "y", should be included in the array you return. The strings "QUICKLY" and "quicklY" are different strings, both ending in a capital wye. You should check whether case matters when applying your inclusion criteria. If capital wye's qualify, you could determine if the last character of a word is "y" or "Y" or *downcase* the word and check if its last character is "y" (`word.downcase[-1] == 'y'`). See [String#downcase](https://ruby-doc.org/core-2.6.3/String.html#method-i-downcase). – Cary Swoveland Jul 23 '19 at 03:00
  • "I could not get to it" is not a precise enough error description for us to help you. *What* doesn't work? *How* doesn't it work? What trouble do you have with your code? Do you get an error message? What is the error message? Is the result you are getting not the result you are expecting? What result do you expect and why, what is the result you are getting and how do the two differ? Is the behavior you are observing not the desired behavior? What is the desired behavior and why, what is the observed behavior, and in what way do they differ? – Jörg W Mittag Jul 23 '19 at 07:12

4 Answers4

2

If I'm understanding your question correctly, you want to return a subset of the passed in array matching your conditions (length ≥ 5 and last character = 'y'). In that case:

def phrases(words)
  words.grep(/.{4}y\z/)
end

What that regex does:

  • .{4} means 4 of any character
  • y is the letter y
  • \z is the end of the string, so we don't match in the middle of a long word

The docs for Enumerable#select are here (an Array is an Enumerable).

Output:

> phrases(["funny", "tidy", "fish", "foogiliously"])
=> ["funny", "foogiliously"]
> phrases(["happily", "lovely", "hello", "multivitaminly"])
=> ["happily", "lovely", "multivitaminly"]

If you only want word characters, rather than any character, you'd use this regex instead: /\A.{4,}y\z/. In that case, \A means the start of the string, and \w{4,} means at least 4 word characters.

magni-
  • 1,934
  • 17
  • 20
  • Okay I will give it a try! Thanks for explaining what regex does. – samibacon Jul 23 '19 at 01:52
  • 1
    Good answer, one regex for both conditions. I suggest `word.match? /.{4}y\z/`, [String#match?](https://ruby-doc.org/core-2.6.3/String.html#method-i-match-3F) (new in Ruby 2.4) possibly being more descriptive, no need for a start-of-string anchor or `,` after `4` and `\z` being an end-of-string anchor (although the end-of-line anchor `$` does work here). OP hasn't told us if you need `/i`. – Cary Swoveland Jul 23 '19 at 03:08
  • 1
    Using `^` and `$` on unpredicted input is extremely dangerous due to chances to produce false positives (when carriage returns are there in the input.) Always use `\A` and `\z` matchers unless you are sure you need `^` and `$`. – Aleksei Matiushkin Jul 23 '19 at 04:01
  • 1
    `Enumerable#select` with a predicate object that responds to `===` is usually better expressed using `Enumerable#grep`, i.e. `words.grep(/\A.{4,}y\z/)` – Jörg W Mittag Jul 23 '19 at 07:14
  • I did not know about `Enumerable#grep`! And I somehow have gone seven years writing Ruby without using `\A` and `\z`... (granted writing regexes is not a daily thing either). Editing both in. More info on `\A` and `\z` vs `^` and `$` [here](https://stackoverflow.com/questions/577653/difference-between-a-z-and-in-ruby-regular-expressions) – magni- Jul 24 '19 at 03:28
1

If, when given an array and inclusion criterion, one wishes to construct an array that contains those elements of the first array that satisfy the inclusion criterion, one generally uses the method Array#select or Array#reject, whichever is more more convenient.

Suppose arr is a variable that holds the given array and include_element? is a method that takes one argument, an element of arr, and returns true or false, depending on whether the inclusion criterion is satisified for that element. For example, say the array comprises the integers 1 through 6 and the inclusion criterion is that the number is even (2, 4 and 6). We could write:

arr = [1,2,3,4,5,6]

def include_element?(e)
  e.even?
end

include_element?(2)
  #=> true 
include_element?(3)
  #=> false 

arr.select { |e| include_element?(e) }
  #=> [2, 4, 6]

The method include_element? is so short we probably would substitute it out and just write:

arr.select { |e| e.even? }

Array#select passes each element of its receiver, arr, to select's block, assigns the block variable e to that value and evaluates the expression in the block (which could be many lines, of course). Here that expresssion is just e.even?, which returns true or false. (See Integer#even? and Integer#odd?.)

If that expression evaluates as a truthy value, the element e is to be included in the array that is returned; if it evaluates as a falsy value, e is not to be included. Falsy values (logical false) are nil and false; truthy values (logical true) are all other Ruby objects, which of course includes true.

Notice that we could instead write:

arr.reject { |e| e.odd? }

Sometimes the inclusion criterion consists of a compound expression. For example, suppose the inclusion criterion were to keep elements of arr that are both even numbers and are at least 4. We would write:

arr.select { |e| e.even? && e >= 4 }
  #=> [4, 6]

With other criteria we might write:

arr.select { |e| e.even? || e >= 4 }
  #=> [2, 4, 5, 6]

or

arr.select { |e| e < 2 || (e > 3 && e < 6) }
  #=> [1, 4, 5]

&& (logical 'and') and || (logical 'or') are operators (search "operator expressions"). As explained at the link, most Ruby operators are actually methods, but these two are among a few that are not.

Your problem now reduces to the following:

arr.select { |str| <length of str is at least 5> && <last character of str is 'y'> }

You should be able to supply code for the <...> bits.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
0

You may want to spend some time reading about the methods in the core library, especially String#end_with? and Enumerable#select. You could then write a method that'd contain something like this:

['abc', 'qwerty', 'asdfghjk', 'y'].select{|s| s.length >= 5}.select{|s| s.end_with? 'y'}

#=> ["qwerty"]
maro
  • 1,506
  • 13
  • 21
  • 1
    Rather than make two passes with `select` consider doing just one. – Cary Swoveland Jul 23 '19 at 03:16
  • Okay thanks! I'm going to experiment and try each different way. – samibacon Jul 23 '19 at 14:51
  • @CarySwoveland, there's a trade-off between readability and "performance". Unless there's a significant (and measured, preferably) performance hit, I'd rather make it more readable. In other words - first make it readable, then optimize as needed. – maro Jul 23 '19 at 19:53
  • I can't disagree in general, but here I would judge the two a toss-up in readability. In ordinary conversion would one say, "select elements that have length of five or more and then from those select those that end in wye" or "select elements that have length of five or more and end in wye"? – Cary Swoveland Jul 23 '19 at 20:15
  • It's obviously a matter of preference, but by having a chain of simple selects you have greater flexibility in adjusting bits and pieces later on, reusing parts of it somewhere else and utilizing all that block/Proc related goodness that Ruby gives us. As a side note, the logic of defining the elements in question ("that have length of five or more and end in 'y'") could be extracted to a separate method to get a simple `list.select{|s| s.is_what_i_want?}` or `list.select(&:is_what_i_want?)` for short. – maro Jul 23 '19 at 20:40
0

You are trying to write a function that should work on a single array at a time I think. Also, you are taking in an array, and retaining only those elements that satisfy your conditions: at least 5 characters long, and ends with y. This is a filtering operation. Read about the methods available for ruby's Array class here

def phrases(array)
  ...
  filtered_array
end

Now the condition you are using is this arr1.length > 5 && arr1.length == "y". The first half should check if the string length is greater than 5, not the array length itself. The second half is an indexing operation, and your code for that is incorrect. basically you are checking if the last character in the string is y.

Usually strings are indexed in this manner: string[index]. In your case you can use string[-1]=='y' or string[string.length - 1]=='y'. This because arrays and strings are zero indexed in ruby. The first element has index of 0, the second has an index of 1, and the last one, therefore, will have an index of length-1. If you use negative indexes then the array is indexed from the end, so string[-1] is a quick way to get to the last element.

Considering this, the function will take the following structure:

def phrases(array)
  filtered_array = [] # an empty array
  loop through the input array
  for each element check for the condition element.length > 5 && element[-1]=='y'
    if true: push the element into the filtered_array
  once the loop is done, return the filtered array
end

Read about ruby arrays, the methods push, filter and select in the above linked documentation to get a better idea. I'd also recommend the codeacademy ruby tutorial.

Edit: Both halves of the condition are incorrect. I had overlooked a mistake in my earlier answer. arr1.length refers to the length of the array. You want to check the length of each string in the array. So in your for loop you should check the length of the loop variable, if that is greater than 5.

Community
  • 1
  • 1
kchak
  • 7,570
  • 4
  • 20
  • 31