Deleting specific string lines of items in array Ruby - arrays

I have an array of 10 items containing of a several lines string like
one string
two string
some string
any string
I want to delete lines containing words some and two. I made code like that:
search_text_domain = %r{some|two}
groups_data.each do |line|
line.each_line do |num|
domain_users_name << (num) unless num =~ search_text_domain
end
end
It works fine but it puts all lines to one big array like
domain_users_name = ["one string", "any string", "big string", "another_s....] and I want tu put it in array of arrays like
domain_users_name = [["one string", "any string"], ["big string", ""another_s...."], [........
I need version that permanently modify groups_data array. Any ideas?

input = ["one string\ntwo string\nsome string\nany string",
"one string\ntwo string\nsome string\nany string"]
input.map { |a| a.split("\n").reject { |e| e =~ %r{some|two} } }
# or
# input.map { |a| a.each_line.map(&:strip).reject { |e| e =~ %r{some|two} } }
# or (smiley-powered version, see the method’s tail)
# input.map { |a| a.each_line.map(&:strip).reject(&%r{some|two}.method(:=~)) }
#⇒ [["one string", "any string"], ["one string", "any string"]]

So you want to delete a group if one of the group elements matches the filter regexp?
groups = [['some', 'word'], ['other', 'word'], ['unrelated', 'list', 'of', 'things']]
filter = %r{word|some}
filtered = groups.delete_if do |group|
group.any? do |word|
word =~ filter
end
end
p filtered
Does this do what you want?

Related

How do I combine elements in an array matching a pattern?

I have an array of strings
["123", "a", "cc", "dddd", "mi hello", "33"]
I want to join by a space consecutive elements that begin with a letter, have at least two characters, and do not contain a space. Applying that logic to the above would yield
["123", "a", "cc dddd", "mi hello", "33"]
Similarly if my array were
["mmm", "3ss", "foo", "bar", "foo", "55"]
I would want the result to be
["mm", "3ss", "foo bar foo", "55"]
How do I do this operation?
There are many ways to solve this; ruby is a highly expressive language. It would be most beneficial for you to show what you have tried so far, so that we can help debug/fix/improve your attempt.
For example, here is one possible implementation that I came up with:
def combine_words(array)
array
.chunk {|string| string.match?(/\A[a-z][a-z0-9]+\z/i) }
.flat_map {|concat, strings| concat ? strings.join(' ') : strings}
end
combine_words(["aa", "b", "cde", "f1g", "hi", "2j", "l3m", "op", "q r"])
# => ["aa", "b", "cde f1g hi", "2j", "l3m op", "q r"]
Note that I was a little unclear exactly how to interpret your requirement:
begin with a letter, have at least two characters, and do not contain a space
Can strings contain punctuation? Underscores? Utf-8 characters? I took it to mean "only a-z, A-Z or 0-9", but you may want to tweak this.
A literal interpretation of your requirement could be: /\A[[:alpha:]][^ ]+\z/, but I suspect that's not what you meant.
Explanation:
Enumerable#chunk will iterate through the array and collect terms by the block's response value. In this case, it will find sequential elements that match/don't match the required regex.
String#match? checks whether the string matches the pattern, and returns a boolean response. Note that if you were using ruby v2.3 or below, you'd have needed some workaround such as !!string.match, to force a boolean response.
Enumerable#flat_map then loops through each "result", joining the strings if necessary, and flattens the result to avoid returning any nested arrays.
Here is another, similar, solution:
def word?(string)
string.match?(/\A[a-z][a-z0-9]+\z/i)
end
def combine_words(array)
array
.chunk_while {|x, y| word?(x) && word?(y)}
.map {|group| group.join(' ')}
end
Or, here's a more "low-tech" solution - which only uses more basic language features. (I'm re-using the same word? method here):
def combine_words(array)
previous_was_word = false
result = []
array.each do |string|
if previous_was_word && word?(string)
result.last << " #{string}"
else
result << string
end
previous_was_word = word?(string)
end
result
end
You can use Enumerable#chunk.
def chunk_it(arr)
arr.chunk { |s|
(s.size > 1) && (s[0].match?(/\p{Alpha}/)) && !s.include?(' ')}.
flat_map { |tf,a| tf ? a.join(' ') : a }
end
chunk_it(["123", "a", "cc", "dddd", "mi hello", "33"])
#=> ["123", "a", "cc dddd", "mi hello", "33"]
chunk_it ["mmm", "3ss", "foo", "bar", "foo", "55"]

Print elements of ruby array line by line after splitting a single string with "\n" condition

I have a single string, achieved using backticks of the following form:
output = `git log`
then, I have splitted the result where there are "\n" and the result went into an array of the form:
array = output.split("\n")
then, I am just trying to output the result in the screen, however, when I am using
array.each do |a|
puts a
end
I am getting as a result a double line of the form:
result after puts
(empty line)
result after puts etc
when my preferred result is a single line of the form:
result after puts
result afters puts etc
I tried to perform this with print, but I am getting:
result after puts result after puts etc
in a single line.
Can you please help me?
The issue is when you split using \n, if there are two \n characters then an empty "" gets added to the array.
eg: test = ["this","","is","test"]
Now if you do,
test.each do |a|
puts a
end
The o/p will be,
this
// a new line will come here.
is
test
so you should reject the empty values,
test2 = test.reject{ |value| value == ""}
test2.each do |a|
puts a
end
Result is,
this
is
test
In same way,
output = `git log`
array = output.split("\n")
array2 = array.reject{ |value| value == ""}
array2.each do |a|
puts a
end
This will give you the correct result.
Thanks to #AndreyDeineko, we have that:
"The issue is when you split using \n if there are two \n characters then an empty "" gets added to the array. Howcome? a = "1\n2\n3\n4\n"; a.split("\n") #=> ["1", "2", "3", "4"].
Therefore, array.each { |a| a } will work for you"
It did not work 100% for me, but using his answer, I manage to achieve the required result which is:
array.each { |a| a }
puts array

Using gsub in array of hashes

I want to remove the spaces in the key value in the hashes
output = [
{"first name"=> "george", "country"=>"Australia"},
{"second name"=> "williams", "country"=>"South Africa"},
{"first name"=> "henry", "country"=>"US"}]
I was able to manage when only one hash was there inside the array with the following code
Array.wrap({}.tap do |hash|
output.each do |key|
key.each do |k, v|
hash[k.gsub(" ","_")] = v
end
end
end)
Please help me to modify the array containing more than one hash.
Note: the output value is dynamic that we cannot hardcode the hash key in the code.
If hash is not nested - you can simply
output.map{|h| h.each_pair.map{|k,v| [k.gsub(' ', '_'), v]}.to_h }
Here's code that will change the spaces to underscores for each key in a hash:
output.flat_map { |h| h.map { |key, v| { key.gsub(" ", "_") => v } } }
=> [{"first_name"=>"george"}, {"country"=>"Australia"}, {"second_name"=>"williams"}, {"country"=>"South Africa"}, {"first_name"=>"henry"}, {"country"=>"US"}]
You cannot modify a hash's keys. You must remove the unwanted key and add a new one. Here's a way of doing both operations in one step (see the doc Hash#delete):
def convert(h)
h.keys.each { |k| (h[k.tr(' ','_')] = h.delete(k)) if k =~ /\s/ }
h
end
Hence:
output.map { |h| convert h }
#=> [{"country"=>"Australia", "first_name"=>"george"},
# {"country"=>"South Africa", "second_name"=>"williams"},
# {"country"=>"US", "first_name"=>"henry"}]
I've used the method String#tr to convert spaces to underscores, but you could use String#gsub as well. Also, you could write k.include?(' ') rather than k =~ /\s/.

How can i get the value from array without using regex in ruby?

From the array of string I need to get string which starts with age- followed by maximum of 2 digit number and optional '+' sign.
Ex: age-1, age-22, age55, age-1+, age-15+
Following is my array:
arr = ["vintage-colllections","age-5"]
or
arr = ["vintage-colllections","age-51+"]
I will extract age "age-5" or "age-51+" from the array.
I tried following things:
arr.find {|e| e.include?"age-"}
Works well for other scenarios but in above the 1st element of array also includes (vint)age- failing there.
arr.find { |e| /age-\d*\+?/ =~ e}
Works fine but I am trying to avoid regex.
Is there any other better approach ?.
Any suggestions are welcome.
Use start_with?:
arr.find { |e| e.start_with?("age-") }
I must grit my teeth to not use a regex, but here goes. I assume the question is as described in a comment I left on the question.
def find_str(arr)
arr.map { |str| str_match(str) }.compact
end
def str_match(str)
return nil unless str[0,4] == "age-"
last = str[-1] == '+' ? -2 : -1
Integer(str[4..last]) rescue return nil
str
end
find_str ["cat", "age-5"] #=> ["age-5"]
find_str ["cat", "age-51+"] #=> ["age-51+"]
find_str ["cat", "age-5t1+"] #=> []
find_str ["cat", "xage-51+"] #=> []

Sort an array that has normal string elements and "number-like" string elements

I have an array that includes float-like strings like "4.5", and regular strings like "Hello". I want to sort the array so that regular strings come at the end and the float-like strings come before them and are sorted by their float value.
I did:
#arr.sort {|a,b| a.to_f <=> b.to_f }
arr = ["21.4", "world", "6.2", "1.1", "hello"]
arr.sort_by { |s| Float(s) rescue Float::INFINITY }
#=> ["1.1", "6.2", "21.4", "world", "hello"]
sort in ruby 1.9+
["1.2", "World", "6.7", "3.4", "Hello"].sort
will return
["1.2", "3.4", "6.7", "Hello", "World"]
You can use #cary solution for certain edge cases eg ["10.0","3.2","hey","world"]
Quick and dirty:
arry = ["1", "world", "6", "21", "hello"]
# separate "number" strings from other strings
tmp = arry.partition { |x| Float(x) rescue nil }
# sort the "numbers" by their numberic value
tmp.first.sort_by!(&:to_f)
# join them all in a single array
tmp.flatten!
Will probably suit your needs

Resources