Ruby merging items in array conditionally - arrays

I have an array containing capital and small letters. I am trying to concatenate capital letters with the following small letters in a new array. For example, I have the following array
first_array = ["A","b","C","d","e"]
and I want to obtain the following array
["Ab","Cde"] #new array
I am trying to iterate through the first array with a code that looks like this:
new_array = []
first_array.each_with_index do |a,index|
if (a!~/^[a-z].*$/)
new_array = new_array.push "#{a}"
else
new_array[-1] = first_array[index-1] + "#{a}" #the idea is to concatenate the small letter with the previous capital letter and replace the last item in the new array
end
but it does not work. I am not sure I am tackling this issue efficiently which is why I can't resolve it. Could somebody suggest some options?

If you join as a string you can then scan to get all the matches:
first_array.join.scan(/[A-Z][a-z]*/)
=> ["Ab", "Cde"]

While I prefer #Paul's answer, you could do the following.
first_array.slice_before { |s| s.upcase == s }.map(&:join)
#=> ["Ab", "Cde"]

So, you want to split your original array when next char is uppercase, and then make strings of those subarrays? There's a method in standard lib that can help you here:
first_array = ["A","b","C","d","e"]
result = first_array.slice_when do |a, b|
a_lower = a.downcase == a
b_upper = b.upcase == b
a_lower && b_upper
end.map(&:join)
result # => ["Ab", "Cde"]

I like Sergio's answer a lot, here's what I brewed up while trying it out:
def append_if_present(lowercase, letters)
lowercase << letters.join if letters.size > 0
end
first_array = ["A","b","C","d","e"]
capitals = []
lowercase = []
letters = []
first_array.each_with_index do |l, i|
if l =~ /[A-Z]/
capitals << l
append_if_present(lowercase, letters)
letters = []
else
letters << l
end
end
append_if_present(lowercase, letters)
p capitals.zip(lowercase).map(&:join)

Here's a method that uses Enumerable#slice_when :
first_array.slice_when{ |a, b| b.upcase == b && a.downcase == a }.map(&:join)

Related

efficient way to get string between the two words

I have a list of strings as:
strings= ['stackasdf:5;iwantthis123jasdoverflow','iwantthistoo','asdf:5;python123jasd']
Now, I want to print strings both in between two substrings(start=asdf:5; end=123jasd) and which are not.
output expected:
iwantthis
iwantthistoo
python
What I've done is:
import re
start = 'asdf:5;'
end = '123jasd'
for i in strings:
t=i[len(start):-len(end)]
if t:
print (t)
else:
print (i)
df:5;iwantthis123jasdo
iwantthistoo
python
I'm not getting the desired output as expected.
Using a regex approach we can try:
strings = ['stackasdf:5;iwantthis123jasdoverflow','iwantthistoo','asdf:5;python123jasd']
output = [re.findall(r'asdf:5;(.*?)123jasd', x)[0] if re.search(r'asdf:5(.*?)123jasd', x) else x for x in strings]
print(output) # ['iwantthis', 'iwantthistoo', 'python']
You can just use a simple for each loop and if statement to print out the correct substring of each element in the array:
strings= ['stackasdf:5;iwantthis123jasdoverflow','iwantthistoo','asdf:5;python123jasd']
for word in strings:
if "asdf:5;" in word and "123jasd" in word:
ind1 = word.index("asdf:5;")
ind2 = word.index("123jasd")
print(word[ind1+7:ind2])
else:
print(word)
output:
iwantthis
iwantthistoo
python
Use replace function & split?
strings = ['stackasdf:5;iwantthis123jasdoverflow',
'iwantthistoo',
'asdf:5;python123jasd']
for i in strings:
c = '\n' # c may be any character that is not present in your strings
i = i.replace('asdf:5;', c).replace('123jasd', c).split(c)
print(i[0] if len(i) == 1 else i[1])
print(f'{strings = }') # to show that I am not changing the original list.
Output:
iwantthis
iwantthistoo
python
strings = ['stackasdf:5;iwantthis123jasdoverflow', 'iwantthistoo', 'asdf:5;python123jasd']

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+"] #=> []

Return unique values of an array without using `uniq`

For a challenge, I'm trying to return the unique values of an array without using uniq. This is what I have so far, which doesn't work:
def unique
unique_arr = []
input_arr.each do |word|
if word != unique_arr.last
unique_arr.push word
end
end
puts unique_arr
end
input = gets.chomp
input_arr = input.split.sort
input_arr.unique
My reasoning here was that if I sorted the array first before I iterated through it with each, I could push it to unique_arr without repetition being a possibility considering if it's a duplicate, the last value pushed would match it.
Am I tackling this the wrong way?
Yes, you are making at least two mistakes.
If you want to call it as input_arr.unique with input_arr being an array, then you have to define the method on Array. You have input_arr within your method body, which comes from nowhere.
puts in the last line of your code outputs to the terminal, but makes the method return nil, which makes it behave differently from uniq.
It can be fixed as:
class Array
def unique
unique_arr = []
each do |word|
unique_arr.push(word) unless unique_arr.last == word
end
unique_arr
end
end
A unique array? That sounds like a Set to me:
require 'set'
Set.new([1,2,3,2,3,4]).to_a
#=> [1,2,3,4]
Here's a concise way to do it that doesn't explicitly use functionality from another class but probably otherwise misses the point of the challenge:
class Array
def unique
group_by(&:itself).keys
end
end
I try this three options. Just for challenge
class Array
def unique
self.each_with_object({}) { |k, h| h[k] = k }.keys
end
def unique2
self.each_with_object([]) { |k, a| a << k unless a.include?(k) }
end
def unique3
arr = []
self.map { |k| arr << k unless arr.include?(k) }
arr
end
end
Here is one more way to do this:
uniques = a.each.with_object([]) {|el, arr| arr << el if not arr.include?(el)}
That's so easy if you see it this way:
a = [1,1,2,3,4]
h = Hash.new
a.each{|q| h[q] = q}
h.values
and this will return:
[1, 2, 3, 4]

Find a Duplicate in an array Ruby

I am trying to find the duplicate values in an array of strings between 1 to 1000000.
However, with the code I have, I get the output as all the entries that are doubled.
So for instance, if I have [1,2,3,4,3,4], it gives me the output of 3 4 3 4 instead of 3 4.
Here is my code:
array = [gets]
if array.uniq.length == array.length
puts "array does not contain duplicates"
else
puts "array does contain duplicates"
print array.select{ |x| array.count(x) > 1}
end
Also, every time I test my code, I have to define the array as array = [1,2,3,4,5,3,5]. The puts works but it does not print when I use array [gets].
Can someone help me how to fix these two problems?
How I wish we had a built-in method Array#difference:
class Array
def difference(other)
h = other.tally
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
though #user123's answer is more straightforward. (Array#difference is probably the more efficient of the two, as it avoids the repeated invocations of count.) See my answer here for a description of the method and links to its use.
In a nutshell, it differs from Array#- as illustrated in the following example:
a = [1,2,3,4,3,2,4,2]
b = [2,3,4,4,4]
a - b #=> [1]
a.difference b #=> [1, 3, 2, 2]
For the present problem, if:
arr = [1,2,3,4,3,4]
the duplicate elements are given by:
arr.difference(arr.uniq).uniq
#=> [3, 4]
For your first problem, you need to uniq function like
array.select{ |x| array.count(x) > 1}.uniq
For your second problem, when you receive a value using array = [gets] it would receive your entire sequence of array numbers as a single string, so everything would be stored in a[0] like ["1, 2 3 4\n"].
puts "Enter array"
array = gets.chomp.split(",").map(&:to_i)
if array.uniq.length == array.length
puts "array does not contain duplicates"
else
puts "array does contain duplicates"
print array.select{ |x| array.count(x) > 1}.uniq
end
copy this code in ruby file and try to run using
ruby file_name.rb
Coming to your 'gets' problem,
When you are doing a gets, your are basically getting a string as an input but not an array.
2.2.0 :001 > array = [gets]
1,2,1,4,1,2,3
=> ["1,2,1,4,1,2,3\n"]
See the above example, how the ruby interpreter took all your elements as a single string and put it in an array as a single array element. So you need to explicitly convert the input to an array with comma as a delimiter. The below will address both your questions.
array = gets.chomp
array = array.split(',').map(&:to_i)
if array.uniq.length == array.length
puts "array does not contain duplicates"
else
puts "array does contain duplicates"
print array.select{ |x| array.count(x) > 1}.uniq!
end

Group strings with similar pattern in Ruby

I have an array of filenames. A subset of these may have similar pattern like this (alphabet strings with a number at the end):
arr = %w[
WordWord1.html
WordWord3.html
WordWord10.html
WordWord11.html
AnotherWord1.html
AnotherWord2.html
FileFile.html
]
How to identify the similar ones (they have identical substring, just their numbers differ) and move them to an array ?
['WordWord1.html', 'WordWord3.html', 'WordWord10.html', 'WordWord11.html']
['AnotherWord1.html', 'AnotherWord2.html']
['FileFile.html']
arr.group_by { |x| x[/[a-zA-Z]+/] }.values
filenames = ["WordWord1.html", "WordWord3.html", "WordWord10.html", "WordWord11.html", "AnotherWord1.html", "AnotherWord2.html", "FileFile.html"]
filenames.inject({}){|h,f|k = f.split(/[^a-zA-Z]/, 2).first;h[k] ||= [];h[k] << f; h}
arr = %w[
WordWord1.html
WordWord3.html
WordWord10.html
WordWord11.html
AnotherWord1.html
AnotherWord2.html
FileFile.html
]
result = {}
arr.each do |a|
prefix = a.match(/[A-Za-z]+/).to_s
if result[prefix]
result[prefix] << a
else
result[prefix] = [a]
end
end
p result

Resources