I have one variable that comes from a map, I'm trying to get a specific part between square brackets
(e.g."dmfkdmfk[IWANTTHISPART]mlkm")
but it isn't working by the way I did. I'm trying as same way used here.
Original Code:
query_values = activities.map do |activity|
'(' +
"#{activity['note']}"
+')'
end
I tried:
query_values = activities.map do |activity|
'(' +
"#{activity['note'].[/#{"["}(.*?)#{"]"}/m, 1]}"
+')'
end
Error log:
syntax error, unexpected '[', expecting '('
'(' + "#{activity['note'].[/#{"["}(.*?)#{"]"}/m, 1]},""'" +')'
^
quase.rb:40: syntax error, unexpected keyword_end, expecting tSTRING_DEND
How can I go through?
Many Thanks.
str = "(dmfkdmfk[IWANTTHISPART]mlkm)"
#1 Use a regex with lookarounds
R0 = /
(?<=\[) # match a left bracket in a positive lookbehind
.+ # match one or more of any character
(?=\]) # match a right bracket in a positive lookahead
/x # free-spacing regex definition mode
(same as R0 = /(?<=\[).+(?=\])/)
str[R0] #=> "IWANTTHISPART"
#2 Split string on a left or right bracket
R1 = /
[\[\]] # match a left or right bracket
/x
(same as R1 = /[\[\]]/)
str.split(R1)[1]
#=> "IWANTTHISPART"
#3 No regex
str[str.index('[')+1..str.index(']')-1]
#=> "IWANTTHISPART"
You cannot have a period after the receiver when using [] in its syntax-sugar form. The following is ungrammatical:
string.[regex, parameter]
Use either the ordinary method invocation form:
string.[](regex, parameter)
or the syntax-sugar form:
string[regex, parameter]
/\[(.*)\]/.match( "dmfkdmfk[IWANTTHISPART]mlkm" )[1]
=> "IWANTTHISPART"
You can use String#[] with a regular expression:
> a = "dmfkdmfk[IWANTTHISPART]mlkm"
> a[/\[.*?\]/][1..-2]
#=> "IWANTTHISPART"
Related
I am very new at Ruby and programming and I am trying to write this simple function below
def sum_square(x=0, y, z=0)
p x**2 + y**2 + z**2
end
sum_square(2,3)
and i get this error syntax error, unexpected '=', expecting ')'
I thought i could use optional argument here
Parameters with default values should be placed after parameters without default values or, as Tom Lord stated in comments, can be "placed anywhere else in the list, so long as they are all defined together". So, if you want to keep y mandatory it should be something like
def sum_square(y, x=0, z=0)
p x**2 + y**2 + z**2
end
But it can be confusing during calls, so you can switch to named params:
def sum_square=(y, x:0, z:0)
p x**2 + y**2 + z**2
end
# all these call are valid
sum_square(1)
sum_square(1, x:2)
sum_square(1, z:2)
sum_square(1, x:2, z:3)
There are more possible ways to implement this function listed in comments with more general approach (for any number of inputs using *) or with all params being named.
I am having a string in Ruby. I need to iterate through the brackets in the string.
My String:
(((MILK AND SOYA) OR NUT) AND COCONUT)
First Iteration should return:
((MILK AND SOYA) OR NUT) AND COCONUT
Second Iteration should return the below:
(MILK AND SOYA) OR NUT
Third Iteration should return the following text:
MILK AND SOYA
How to do this in Ruby? Thanks in advance for the help.
thy this solution:
str = "(((MILK AND SOYA) OR NUT) AND COCONUT)"
while not str.nil?
puts str = str.match(/\((.*)\)/).to_a.last
end # =>
((MILK AND SOYA) OR NUT) AND COCONUT
(MILK AND SOYA) OR NUT
MILK AND SOYA
regex /\((.*)\)/ searches for string inside brackets
gsub and regex
#DmitryCat's solution works fine with your example, but it seems you might be interested in the innermost brackets first.
So you'll need to make sure the characters between brackets aren't brackets :
str = "(((MILK AND SOYA) OR NUT) AND COCONUT)"
while str.gsub!(/\(([^\(\)]+)\)/){ p $1; ''}
end
# "MILK AND SOYA"
# " OR NUT"
# " AND COCONUT"
With "((MILK AND SOYA) OR (MILK AND NUT))"
it outputs :
# "MILK AND SOYA"
# "MILK AND NUT"
# " OR "
Boolean logic to tree
With parser gem
Regexen probably aren't the right tool for this job.
This parser gem would have no problem analysing your expression :
require 'parser/current'
str = "(((MILK AND SOYA) OR NUT) AND COCONUT)"
p Parser::CurrentRuby.parse(str.gsub(/\b(and|or)\b/i){|s| s.downcase})
# s(:begin,
# s(:and,
# s(:begin,
# s(:or,
# s(:begin,
# s(:and,
# s(:const, nil, :MILK),
# s(:const, nil, :SOYA))),
# s(:const, nil, :NUT))),
# s(:const, nil, :COCONUT)))
You now have a tree : a Root node and children method. Which you can call recursively to get any information about your expression.
With sexp gem
(Thanks to #Casper for this suggestion)
It looks like sexp gem might also work, possibly with an easier syntax than parser :
require 'sxp'
p SXP.read("(((MILK AND SOYA) OR (NUT AND SOYA)) AND COCONUT)")
# [[[:MILK, :AND, :SOYA], :OR, [:NUT, :AND, :SOYA]], :AND, :COCONUT]
Sphinx
As mentioned by #Casper in the comments (thanks again!), you're trying to reinvent the wheel. If you need full text search for Rails with boolean expressions, Sphinx is a great tool. It's fast, good, reliable and there's an adapter for Ruby/Rails : thinkingsphinx.
Use index to get the first character '(' and rindex to find the closing character ')':
s = "(((MILK AND SOYA) OR NUT) AND COCONUT)"
s = s.slice(s.index('(') + 1, s.rindex(')') - 1) unless s.index('(').nil? || s.rindex(')').nil?
With this code you will get all the string you need. Just call it in a loop until s is nil
I hope it helps
I'm trying to find the best way to determine the letter count in an array of strings. I'm splitting the string, and then looping every word, then splitting letters and looping those letters.
When I get to the point where I determine the length, the problem I have is that it's counting commas and periods too. Thus, the length in terms of letters only is inaccurate.
I know this may be a lot shorter with regex, but I'm not well versed on that yet. My code is passing most tests, but I'm stuck where it counts commas.
E.g. 'You,' should be string.length = "3"
Sample code:
def abbr(str)
new_words = []
str.split.each do |word|
new_word = []
word.split("-").each do |w| # it has to be able to handle hyphenated words as well
letters = w.split('')
if letters.length >= 4
first_letter = letters.shift
last_letter = letters.pop
new_word << "#{first_letter}#{letters.count}#{last_letter}"
else
new_word << w
end
end
new_words << new_word.join('-')
end
new_words.join(' ')
I tried doing gsub before looping the words, but that wouldn't work because I don't want to completely remove the commas. I just don't need them to be counted.
Any enlightenment is appreciated.
arr = ["Now is the time for y'all Rubiests",
"to come to the aid of your bowling team."]
arr.join.size
#=> 74
Without a regex
def abbr(arr)
str = arr.join
str.size - str.delete([*('a'..'z')].join + [*('A'..'Z')].join).size
end
abbr arr
#=> 58
Here and below, arr.join converts the array to a single string.
With a regex
R = /
[^a-z] # match a character that is not a lower-case letter
/ix # case-insenstive (i) and free-spacing regex definition (x) modes
def abbr(arr)
arr.join.gsub(R,'').size
end
abbr arr
#=> 58
You could of course write:
arr.join.gsub(/[^a-z]/i,'').size
#=> 58
Try this:
def abbr(str)
str.gsub /\b\w+\b/ do |word|
if word.length >= 4
"#{word[0]}#{word.length - 2}#{word[-1]}"
else
word
end
end
end
The regex in the gsub call says "one or more word characters preceded and followed by a word boundary". The block passed to gsub operates on each word, the return from the block is the replacement for the 'word' match in gsub.
You can check for each character that whether its ascii value lies in 97-122 or 65-90.When this condition is fulfilled increment a local variable that will give you total length of string without any number or any special character or any white space.
You can use something like that (short version):
a.map { |x| x.chars.reject { |char| [' ', ',', '.'].include? char } }
Long version with explanation:
a = ['a, ', 'bbb', 'c c, .'] # Initial array of strings
excluded_chars = [' ', ',', '.'] # Chars you don't want to be counted
a.map do |str| # Iterate through all strings in array
str.chars.reject do |char| # Split each string to the array of chars
excluded_chars.include? char # and reject excluded_chars from it
end.size # This returns [["a"], ["b", "b", "b"], ["c", "c"]]
end # so, we take #size of each array to get size of the string
# Result: [1, 3, 2]
I am having trouble writing this so that it will take a sentence as an argument and perform the translation on each word without affecting the punctuation.
I'd also like to continue using the partition method.
It would be nice if I could have it keep a quote together as well, such as:
"I said this", I said.
would be:
"I aidsay histay", I said.
def convert_sentence_pig_latin(sentence)
p split_sentence = sentence.split(/\W/)
pig_latin_sentence = []
split_sentence.each do |word|
if word.match(/^[^aeiou]+/x)
pig_latin_sentence << word.partition(/^[^aeiou]+/x)[2] + word.partition(/^[^aeiou]+/x)[1] + "ay"
else
pig_latin_sentence << word
end
end
rejoined_pig_sentence = pig_latin_sentence.join(" ").downcase + "."
p rejoined_pig_sentence.capitalize
end
convert_sentence_pig_latin("Mary had a little lamb.")
Your main problem is that [^aeiou] matches every character outside that range, including spaces, commas, quotation marks, etc.
If I were you, I'd use a positive match for consonants, ie. [b-df-hj-np-tv-z] I would also put that regex in a variable, so you're not having to repeat it three times.
Also, in case you're interested, there's a way to make your convert_sentence_pig_latin method a single gsub and it will do the whole sentence in one pass.
Update
...because you asked...
sentence.gsub( /\b([b-df-hj-np-tv-z])(\w+)/i ) { "#{$2}#{$1}ay" }
# iterate over and replace regexp matches using gsub
def convert_sentence_pig_latin2(sentence)
r = /^[^aeiou]+/i
sentence.gsub(/"([^"]*)"/m) {|x| x.gsub(/\w+/) {|y| y =~ r ? "#{y.partition(r)[2]}#{y.partition(r)[1]}ay" : y}}
end
puts convert_sentence_pig_latin2('"I said this", I said.')
# define instance method: String#to_pl
class String
R = Regexp.new '^[^aeiou]+', true # => /^[^aeiou]+/i
def to_pl
self.gsub(/"([^"]*)"/m) {|x| x.gsub(/\w+/) {|y| y =~ R ? "#{y.partition(R)[2]}#{y.partition(R)[1]}ay" : y}}
end
end
puts '"I said this", I said.'.to_pl
sources:
http://www.ruby-doc.org/core-2.1.0/Regexp.html
http://ruby-doc.org/core-2.0/String.html#method-i-gsub
unless (place =~ /^\./) == 0
I know the unless is like if not but what about the condtional?
=~ means matches regex
/^\./ is a regular expression:
/.../ are the delimiters for the regex
^ matches the start of the string or of a line (\A matches the start of the string only)
\. matches a literal .
It checks if the string place starts with a period ..
Consider this:
p ('.foo' =~ /^\./) == 0 # => true
p ('foo' =~ /^\./) == 0 # => false
In this case, it wouldn't be necessary to use == 0. place =~ /^\./ would suffice as a condition:
p '.foo' =~ /^\./ # => 0 # 0 evaluates to true in Ruby conditions
p 'foo' =~ /^\./ # => nil
EDIT: /^\./ is a regular expression. The start and end slashes denotes that it is a regular expression, leaving the important bit to ^\.. The first character, ^ marks "start of string/line" and \. is the literal character ., as the dot character is normally considered a special character in regular expressions.
To read more about regular expressions, see Wikipedia or the excellent regular-expressions.info website.