Programmatic way to obtain Ruby keywords - ruby

I have an Array in Ruby, with all keywords.
For instance:
RUBY_KEYWORDS = %w(
alias and BEGIN begin break case class def defined
do else elsif END end ensure false for if in module
next nil not or redo rescue retry return self super
then true undef unless until when while yield
)
My question is simple:
Is there an in-built way to programmatically access all keywords?
Some of my projects need to run a query against user input,
and it's a bit annoying to have to define the same array in
all these projects.

Try this code :)
RubyToken::TokenDefinitions.select { |definition| definition[1] == RubyToken::TkId }
.map { |definition| definition[2] }
.compact
.sort
# returns :
# ["BEGIN", "END", "__FILE__", "__LINE__", "alias", "and", "begin", "break", "case", "class", "def", "defined?", "do", "else", "elsif", "end", "ensure", "false", "for", "if", "in", "module", "next", "nil", "not", "or", "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless", "until", "when", "while", "yield"]

I don't think you can, since it will be defined in the parser.
Your alternative would be to look at the source code: https://github.com/ruby/ruby/blob/ruby_2_1/defs/keywords

Related

How to check multiples itens of an array?

everyone, I'm trying to check an information from every item of an array but I don't know how to do this for all the 4 items, first that's the standard response:
data: [{"optinId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "string", "description": string", "alertMessage": "string", "master": true, "optin": true}]
this example has only 1 item but the true I have has 4, what I have to do is "expect when MASTER=true then OPTIN=true", I started with a simple IF:
if (response["data"][0]["master"]=true)
expect(#response["data"][0]["optin"]).to eql true
end
that solution isn't enough for me because I want to check all 4 items of the array, can someone please help me with a solution?
You can find the item you want with .find method and assert the fields you want.
item = response[:data].find { |item| item[:master] }
expect(item&.dig(:optin)).to eql(true)
This code will assert there must be a item with master true and optin true
try iterating through items in the array, using a for loop or for each
example:
arr = [{"optinId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "string", "description": string", "alertMessage": "string", "master": true, "optin": true}]
arr.each do |item|
/** your code **/
end
item = #response["data"].find { |item| item["master"]==true }
expect(item["optin"]).to eql true
that solved my problem, thanks to Mehmet Adil Istikbal and everyone else that contribute

Split a string delimited by a list of substrings

I have data like:
str = "CODEA text for first item CODEB text for next item CODEB2 some"\
"more text CODEC yet more text"
and a list:
arr = ["CODEA", "CODEB", "CODEB2", "CODEC", ... ]
I want to divide this string into a hash. The keys of the hash will be CODEA, CODEB, etc. The values of the hash will be the text that follows, until the next CODE. The output should look like this:
"CODEA" => "text for first item",
"CODEB" => "text for next item",
"CODEB2" => "some more text",
"CODEC" => "yet more text"
We are given a sting and an array.
str = "CODEA text for first item CODEB text for next item " +
"CODEB2 some more text CODEC yet more text"
arr= %w|CODEC CODEB2 CODEA CODEB|
#=> ["CODEC", "CODEB2", "CODEA", "CODEB"]
This is one way to obtain the desired hash.
str.split.
slice_before { |word| arr.include?(word) }.
map { |word, *rest| [word, rest.join(' ')] }.
to_h
#=> {"CODEA" =>"text for first item",
# "CODEB" =>"text for next item",
# "CODEB2"=>"some more text",
# "CODEC" =>"yet more text"}
See Enumerable#slice_before.
The steps are as follows.
a = str.split
#=> ["CODEA", "text", "for", "first", "item", "CODEB",
# "text", "for", "next", "item", "CODEB2", "some",
# "more", "text", "CODEC", "yet", "more", "text"]
b = a.slice_before { |word| arr.include?(word) }
#=> #<Enumerator:
# #<Enumerator::Generator:0x00005cbdec2b5eb0>:each>
We can see the (4) elements (arrays) that will be generated by this enumerator and passed to each_with_object by converting it to an array.
b.to_a
#=> [["CODEA", "text", "for", "first", "item"],
# ["CODEB", "text", "for", "next", "item"],
# ["CODEB2", "some", "more", "text"],
# ["CODEC", "yet", "more", "text"]]
Continuing,
c = b.map { |word, *rest| [word, rest.join(' ')] }
#=> [["CODEA", ["text for first item"]],
# ["CODEB", ["text for next item"]],
# ["CODEB2", ["some more text"]],
# ["CODEC", ["yet more text"]]]
c.to_h
#=> {"CODEA"=>"text for first item",
# "CODEB"=>"text for next item",
# "CODEB2"=>"some more text",
# "CODEC"=>"yet more text"}
The following is perhaps a better way of doing this.
str.split.
slice_before { |word| arr.include?(word) }.
each_with_object({}) { |(word, *rest),h|
h[word] = rest.join(' ') }
When I was a kid this might be done as follows.
last_word = ''
str.split.each_with_object({}) do |word,h|
if arr.include?(word)
h[word]=''
last_word = word
else
h[last_word] << ' ' unless h[last_word].empty?
h[last_word] << word
end
end
last_word must be set to anything outside the block.
Code:
str = 'CODEA text for first item CODEB text for next item ' +
'CODEB2 some more text CODEC yet more text'
puts Hash[str.scan(/(CODE\S*) (.*?(?= CODE|$))/)]
Result:
{"CODEA"=>"text for first item", "CODEB"=>"text for next item", "CODEB2"=>"some more text", "CODEC"=>"yet more text"}
Another option.
string.split.reverse
.slice_when { |word| word.start_with? 'CODE' }
.map{ |(*v, k)| [k, v.reverse.join(' ')] }.to_h
Enumerator#slice_when, in this case returns this array:
[["text", "more", "yet", "CODEC"], ["text", "more", "some", "CODEB2"], ["item", "next", "for", "text", "CODEB"], ["item", "first", "for", "text", "CODEA"]]
Then the array is mapped to build the required hash to get the result (I did not reversed the Hash):
#=> {"CODEC"=>"yet more text", "CODEB2"=>"some more text", "CODEB"=>"text for next item", "CODEA"=>"text for first item"}
Adding parentheses to the pattern in String#split lets you get both the separators and the fields.
str.split(/(#{Regexp.union(*arr)})/).drop(1).each_slice(2).to_h
# =>
# {
# "CODEA"=>" text for first item ",
# "CODEB"=>"2 somemore text ",
# "CODEC"=>" yet more text"
# }

Remove elements from a hash array if a value matches a line in a text file

I have a JSON file with a hash in that looks like this
{
"1": {
"organisation": "Example1",
"name": "Name1",
"items": [
{
"name": "Name1",
"html_url": "URL1",
"results": [
"items go here"
]
},
{
"name": "Name2",
"html_url": "URL2",
"results": [
"Items go here"
]
},
I want to delete elements whose html_url matches a list stored in a text file, I have been toying with a delete-if like this.
#org.each do |key,value|
File.open('views/investigated.txt').each do |line|
value['items'].delete_if { |h| h['html_url'] == line }
end
end
but it doesn't seem to alter the array, I'm completely lost, any help would be much appreciated
#Stefan is right, it's better practice to create a separate array with the lines you'd like to delete from the array:
urls_to_delete = []
File.open('views/investigated.txt').each do |line|
urls_to_delete.push(line)
end
Then use that array to remove the lines from the hash:
#org.each do |key,value|
value['items'].delete_if { |h| urls_to_delete.include?(h['html_url']) }
end
Tested it with your example and does exactly what you're trying to achieve.
Maybe you can delete records in one iteration by using each_key on ˙#org˙ and you dont need new array.
Like this:
#org.each_key do |key|
File.open('views/investigated.txt').each do |line|
line.chomp!
#org[key]['items'].delete_if { |h| h['html_url'] == line }
end
end

How to make a method to split two strings in an array?

I'm trying to return two strings in an array into individual words:
list = ['hello my name is ryan', 'hole me llamo']
def splitter(inp)
inp.each.split(' ')
end
print splitter(list)
This returns:
ruby splitter.rb
splitter.rb:4:in `splitter': undefined method `strsplit' for # <Enumerator: ["hello my name is ryan", "hole me llamo"]:each> (NoMethodError)
from splitter.rb:7:in `<main>'
It works if I don't use .each and use inp(0) or inp(1) but only one string returns.
How can I get both strings to be returned?
Here is one you should do :
def splitter(inp)
inp.flat_map(&:split)
end
splitter list
# => ["hello", "my", "name", "is", "ryan", "hole", "me", "llamo"]
In your code inp.each was actually a method call like Array#each, which without a block gives an Enumerator. And String#spilt does exist, but there is not method like Enumerator#split, that's why NoMethod error blows up.
And if you want the array of words for each individual strings, then
def splitter(inp)
inp.map(&:split)
end
splitter list
# => [["hello", "my", "name", "is", "ryan"], ["hole", "me", "llamo"]]
If I understand the question correctly, it's just:
list = ['hello my name is ryan', 'hole me llamo']
list.join(' ').split
#=> ["hello", "my", "name", "is", "ryan", "hole", "me", "llamo"]

Ruby separating an array of strings by checking whether or not the object inherits from a certain class

I have an array of strings that I read from a file x
I have an empty array y
Some string objects are integers
How do I separate the integers from the strings, specifically by using a call to_a?
Right now i'm trying
x.each do |s|
if s.to_i.is_a?(Integer)
y << s
end
end
but this just converts everything to an integer and stuffs it in y, is there a way to see if an object is truly from the Integer class?
Edit add sample input/output
x = [ "This", "is", "a", "random", "amalgamation", "of", "text", "and", "a",
"bunch", "of", "numbers", "111113087403957304739703975", "how", "can", "I",
"read", "this", "in." ]
y = [ 111113087403957304739703975 ]
x = [ "This", "is", "a", "random", "amalgamation", "of", "text", "and", "a",
"bunch", "of", "numbers", "111113087403957304739703975", "how", "can", "I",
"read", "this", "in." ]
y = [ 111113087403957304739703975 ]
def extract_integers(array)
array.select { |v| v.match(/\A\d+\z/) }.map(&:to_i)
# or (simpler, as suggested by #theTinMan)
array.reject { |v| v[/\D/] }.map(&:to_i)
end
p extract_integers(x) #=> [111113087403957304739703975]
p extract_integers(x) == y #=> true
s.match(/^\d+$/) will match a string containing only numbers, so you can use this to test your strings against
You might use Enumerable#grep:
arr = %w[9 cats on 33 hot tin roofs]
#=> ["9", "cats", "on", "33", "hot", "tin", "roofs"]
arr.grep /^\d+$/
#=> ["9", "33"]
arr.grep(/^\d+$/).map(&:to_i)
#=> [9, 33]
x.each do |s|
begin
Integer(s)
rescue ArgumentError
else
y << s
end
end
If applied on a string that doesn't parse as an integer, Integer() raises an ArgumentError. You can use this to find integer strings.
It's always interesting, and useful to run benchmarks:
require 'fruity'
x = [ "This", "is", "a", "random", "amalgamation", "of", "text", "and", "a",
"bunch", "of", "numbers", "111113087403957304739703975", "how", "can", "I",
"read", "this", "in." ]
def extract_integers(array)
array.select { |v| v.match(/\A\d+\z/) }.map(&:to_i)
end
def extract_integers_reject(array)
array.reject { |v| v[/\D/] }.map(&:to_i)
end
compare do
use_exception {
y = []
x.each do |s|
begin
Integer(s)
rescue ArgumentError
else
y << s.to_i
end
end
y
}
use_extract_integers {
extract_integers(x)
}
use_extract_integers_reject {
extract_integers_reject(x)
}
end
Running that results in the following on my machine:
Running each test 256 times. Test will take about 1 second.
use_extract_integers_reject is faster than use_extract_integers by 30.000000000000004% ± 10.0%
use_extract_integers is faster than use_exception by 6x ± 0.1
Note, y << s was changed to y << s.to_i to make the outputs all match.
I'd probably simplify the code using the ArgumentError rescue like this:
x.each do |s|
begin
y << Integer(s)
rescue ArgumentError
end
end

Resources