Ruby Strings - Checking against a set of strings to match - ruby

I am trying to check an array of strings for containing one or more matching strings.
Currently I am doing this by using if statements - not nice, but it works - However now I am looking for a more Ruby-like way to do this.
row[:datapoints].each do |data|
if data[:direction].include? "Beusselstr"
data[:image] = "category-1"
end
if data[:direction].include? "Ostkreuz"
data[:image] = "category-1"
end
if data[:direction].include? "Westend"
data[:image] = "category-2"
end
if data[:direction].include? "1)S Gr"
data[:image] = "category-3"
end
end
Instead of this I'd like to store the matching strings in an array. To make it a bit more complicated I actually have different categories of matching terms with their own result actions (see category specific assignment of the data[:image] value).
category_1_keywords = ["Beusselstr","Ostkreuz"]
category_2_keywords = ["nefeld Bhf","Greifswalder","Westend"]
category_3_keywords = ["1)S Gr"]
imagecategories = {:category_1 => category_1_keywords,:category_2 => category_2_keywords,:category_3 => category_3_keywords}
How would filtering the array (row[:datapoints]) using such a matching array (imagecategories) look like?

You would probably want to use the array intersection operator & and check if it's empty.
if (data[:direction] & category_1_keywords).any?
data[:image] = "category-1"
end
4 if's in a row though start looking like time for an iterator:
keywords = {
'category_1' => ["Beusselstr","Ostkreuz"],
'category_2' => ["nefeld Bhf","Greifswalder","Westend"],
'category_3' => ["1)S Gr"]
}
data[:image] = keywords.find{|k,v| (data[:direction] & v).any?}[0]

Assuming that data[:direction] is a string, you can find the category with:
data[:image], _ = imagecategories.find { |category, keywords|
keywords.any? { |keyword|
data[:direction].include?(keyword)
}
}
In plain text: find the first category-keywords pair (imagecategories.find) having any keyword (keywords.any?) that the data[:direction] string contains.

Related

How to check that one word appears after other in cypress?

In cypress I have
Book
:This is the book of
English
how to test that 'Book:This is book of English' is in order ?
I not having single row I have multiple rows like this
cy.get('span').then((Val)=>{ const text=val.text().trim(':').toString(); });
The answer from #AsadMusharaf needs an extra step:
cy.get('span')
.then($span => {
return $span.text() // extract test
.split(': ') // split it at ": "
})
.should('deep.equal', ['Book', 'English']) // compare to ordered array
You have bold <b> elements, will need to add b in the selector
cy.get('span b')
.invoke('text')
.should('eq', 'BookEnglish')
cy.get('span').then(span => {
const words = span.split(':');
const firstword = words[0]
const secondword = words[1]
cy.log(firstword, secondword);
Try this. Here we are splitting the pair of words by ":" and storing them in array vairabled and calling that variables. Array [0] will have the text "Book" and array [1] will have the text "English"
you could just see if the whole string matches with contains after or as part of get
cy.get('span').contains('Book: English').should('exist')
// or
cy.get('span:contains("Book: English")').should('exist')

Insert multiple characters in string at once

Where as str[] will replace a character, str.insert will insert a character at a position. But it requires two lines of code:
str = "COSO17123456"
str.insert 4, "-"
str.insert 7, "-"
=> "COSO-17-123456"
I was thinking how to do this in one line of code. I came up with the following solution:
str = "COSO17123456"
str.each_char.with_index.reduce("") { |acc,(c,i)| acc += c + ( (i == 3 || i == 5) ? "-" : "" ) }
=> "COSO-17-123456
Is there a built-in Ruby helper for this task? If not, should I stick with the insert option rather than combining several iterators?
Use each to iterate over an array of indices:
str = "COSO17123456"
[4, 7].each { |i| str.insert i, '-' }
str #=> "COSO-17-123456"
You can uses slices and .join:
> [str[0..3], str[4..5],str[6..-1]].join("-")
=> "COSO-17-123456"
Note that the index after the first one (between 3 and 4) will be different since you are not inserting earlier insertion first. ie, more natural (to me anyway...)
You will insert at the absolute index of the original string -- not the moving relative index as insertions are made.
If you want to insert at specific absolute index values, you can also use ..each_with_index and control the behavior character by character:
str2 = ""
tgts=[3,5]
str.split("").each_with_index { |c,idx| str2+=c; str2+='-' if tgts.include? idx }
Both of the above create a new string.
String#insert returns the string itself.
This means you can chain the method calls, which can be a prettier and more efficient if you only have to do it a couple of times like in your example:
str = "COSO17123456".insert(4, "-").insert(7, "-")
puts str
COSO-17-123456
Your reduce version can be therefore more concisely written as:
[4,7].reduce(str) { |str, idx| str.insert(idx, '-') }
I'll bring one more variation to the table, String#unpack:
new_str = str.unpack("A4A2A*").join('-')
# or with String#%
new_str = "%s-%s-%s" % str.unpack("A4A2A*")

Create regular expression from Array of search terms ruby

Is there a way / gem to create regular expressions with some basic search parameters.
e.g.
Search = ["\"German Shepherd\"","Collie","poodle", "Miniature Schnauzer"]
Such that the regexp will search (case insensitively) for:
"German Shepherd" - exactly
OR
"Collie"
OR
"poodle"
OR
"Miniature" AND "Schnauzer"
So in this case something like:
/German\ Shepherd|Collie|poodle|(?=.*Miniature)(?=.*Schnauzer).+/i
(Open to suggestions of better ways of doing the last bit...)
If I understood the question properly, here you go:
regexps = ["\"German Shepherd\"","Collie","poodle", "Miniature Schnauzer"]
# those in quotes
greedy = regexps.select { |re| re =~ /\A['"].*['"]\z/ } # c'"mon, parser
# the rest unquoted
non_greedy = (regexps - greedy).map(&:split).flatten
# concatenating... ⇓⇓⇓ get rid of quotes
all = Regexp.union(non_greedy + greedy.map { |re| re[1...-1] })
#⇒ /Collie|poodle|Miniature|Schnauzer|German\ Shepherd/
UPD
I finally got what is to be done with Miniature Schnauzer (please see a comment below for further explanation.) That said, these words are to be permuted and joined with non-greedy .*?:
non_greedy = (regexps - greedy).map(&:split).map do |re|
# single word? YES : NO, permute and join
re.length < 2 ? re : re.permutation.map { |p| Regexp.new p.join('.*?') }
end.flatten
all = Regexp.union(non_greedy + greedy.map { |re| re[1...-1] })
#=> /Collie|poodle|(?-mix:Miniature.*?Schnauzer)|(?-mix:Schnauzer.*?Miniature)|German\ Shepherd/

Parse file, find a string and store next values

I need to parse a file according to different rules.
The file contains several lines.
I go through the file line by line. When I find a specific string, I have to store the data present in the next lines until a specific character is found.
Example of file:
start {
/* add comment */
first_step {
sub_first_step {
};
sub_second_step {
code = 50,
post = xxx (aaaaaa,
bbbbbb,
cccccc,
eeeeee),
number = yyyy (fffffff,
gggggg,
jjjjjjj,
ppppppp),
};
So, in this case:
File.open(#file_to_convert, "r").each_line do |line|
In "line" I have my current line. I need to:
1) find when the line contains the string "xxx"
if line.include?("union") then
Correct?
2) store the next values (e.g.: aaaa, bbbb, ccccc,eeee) in an array until I find the character ")". This highlights that the section is finished.
I think we I reach the line with the string "xxxx" I have to iterate the next lines inside the block "if".
Try this:
file_contents = File.read(#file_to_convert)
lines = file_contents[/xxx \(([^)]+)\)/, 1].split
# => ["aaaaaa,", "bbbbbb,", "cccccc,", "eeeeee"]
The regex (xxx \(([^)]+)\)) takes all the text after xxx ( until the next ), and split splits it into its items.
It think this is what you are looking for:
looking = true
results = []
File.open(#file_to_convert, "r").each_line do |line|
if looking
if line.include?("xxx")
looking = false
results << line.scan(/\(([^,]*)/x)
end
else
if line.include?(")")
results << line.strip.delete('),')
break
else
results << line.strip.delete(',')
end
end
end
puts results

ruby find and replace portion of string

I have a large file in a ruby variable, it follows a common pattern like so:
// ...
// comment
$myuser['bla'] = 'bla';
// comment
$myuser['bla2'] = 'bla2';
// ...
I am trying to given a 'key' replace the 'value'
This replaces the entire string how do I fix it? Another method I thought is to do it in two steps, step one would be to find the value within the quotes then to perform a string replace, what's best?
def keyvalr(content, key, value)
return content.gsub(/\$bla\[\'#{key}\'\]\s+\=\s+\'(.*)\'/) {|m| value }
end
The .* is greedy and consumes as much as possible (everything until the very last '). Make that . a [^'] then it is impossible for it to go past the first closing '.
/(\$bla\[\'#{key}\'\]\s+\=\s+\')[^']*(\')/
I also added parentheses to capture everything except for the value, which is to be replaced. The first set of parens will correspond to \1 and the second to \2. So that you replace the match of this with:
"\1yournewvaluehere\2"
I'd use something like:
text = %q{
// ...
// comment
$myuser['bla'] = 'bla';
// comment
$myuser['bla2'] = 'bla2';
// ...
}
from_to = {
'bla' => 'foo',
'bla2' => 'bar'
}
puts text.gsub(/\['([^']+)'\] = '([^']+)'/) { |t|
key, val = t.scan(/'([^']+)'/).flatten
"['%s'] = '%s'" % [ key, from_to[key] ]
}
Which outputs:
// ...
// comment
$myuser['bla'] = 'foo';
// comment
$myuser['bla2'] = 'bar';
// ...
This is how it works:
If I do:
puts text.gsub(/\['([^']+)'\] = '([^']+)'/) { |t|
puts t
}
I see:
['bla'] = 'bla'
['bla2'] = 'bla2'
Then I tried:
"['bla'] = 'bla'".scan(/'([^']+)'/).flatten
=> ["bla", "bla"]
That gave me a key, "value" pair, so I could use a hash to look-up the replacement value.
Sticking it inside a gsub block meant whatever matched got replaced by my return value for the block, so I created a string to replace the "hit" and let gsub do its "thang".
I'm not a big believer in using long regex. I've had to maintain too much code that tried to use complex patterns, and got something wrong, and failed to accomplish what was intended 100% of the time. They're very powerful, but maintenance of code is a lot harder/worse than developing it, so I try to keep patterns I write in spoon-size pieces, having mercy on those who follow me in maintaining the code.

Resources