re-organize array element in my case - ruby-on-rails

Suppose I have an array of string like following:
str_arr=["10$", "10$ I am a student","10$Good","10$ Nice weekend!"]
I would like to re-organize the array element value in the way that, in each element of the array , if there is(are) white space(s) after 10$ sign, then, combine the 10$ with the following word.
That's generating a new array like following:
str_arr=["10$", "10$I am a student","10$Good","10$Nice weekend!"]
What I tried to do is like following:
str_arra.map{|elem|
# not sure how to do here,
#split and check then combine again?
if elem.size>1
words=elem.split()
if words[0]=='10$'
#not sure how to do here
end
elsif elem.size==1
elem
end
}
but not sure how to generate the new array ... and the code above seems verbose...
P.S. it is possible that there are multiple white-spaces after 10$, then comes a word

If you only have those cases, the following should do the trick:
["$ abc", "$str"].map {|v, k| v.sub(/\$ +/, '$')}
Here's an example: http://codepad.org/XHeo7E8B

Do this:
str_arra.map{|elem| elem.gsub(/^\$ /, "$") }

Related

Multiple LIKE and AND operators in RAILS/POSTGRESQL

I'm using Rails 5.0.1 and Postgresql as my database. I have a table with column :content which contains words.
The problem: When I'm looking for a specific word, I want to see if the word contains letters (chars) of my choice. Let's say i want to DB to return words containg letters "a", "b" and "c" (all of them, but with no specific order)
What I'm doing: I found that i could use
Word.where("content like ?", "%a%").where("content like ?", "%b%").where("content like ?", "%c%")
OR
Word.where("content like ? content like ? content like ?", "%a%", "%b%", "%c%")
In both cases even if i switch order of given letters/substrings it works fine, ex. both would find word "back", "cab" etc..
The question: Is there any better/more DRY way to do it? What if want to find word with 8 different letters? Do i have to use "content like ?" 8 times? Is it possible to pass arguments as an array? (let's assume i don't know how many letters user will input)
PostgreSQL has a handy expr op all (array) expression so you can say things like:
where content like all (array['%a%', '%b%', '%c'])
as a short form of:
where content like '%a%'
and content like '%b%'
and content like '%c%'
Also, ActiveRecord will conveniently replace a ? placeholder with a comma-delimited list if you hand it a Ruby array. That lets you say things like:
Word.where('content like all (array[?])', %w[a b c].map { |c| "%#{c}%" })
and:
Word.where('content like all (array[?])', some_other_array.map { |c| "%#{c}%" })
I found a solution:
letters = ["%a%", "%b%", "%c%"]
Word.where((['content LIKE ?'] * letters.size).join(' AND '), *letters)
This is easy and much better than I was using.
I think the SIMILAR TO operator might help. It allows you to pass in a regular expression that you could construct on the fly.
letters = ['a', 'b', 'c']
pattern = "%(#{letters.join('|')})%"
Word.where("content SIMILAR TO ?", pattern)

Order array depending on appearance in another

Lets say I have 2 arrays of users.
[user1, user2, user3]
[user3]
Based on the second array, I want to sort the first array so that occurrences in the second array appear first in the first array.
So the result of the first array would be:
[user3, user1, user2]
I realise the simple way would be to iterate over the first array and populate an empty array, ordering it if the second array contains it, then merging the rest. The below is pseudo code and untested, but gives an idea of what I was thinking as a simple solution
return_array = []
array1.each do |a|
if array2.include? a
return_array.push array1.pop(a)
end
end
return_array.merge array1
Is there any way to refine this? Built in rails or ruby methods for example.
You should use array intersection and array difference:
a&b + a-b
would give you what you're looking for.
The manual for intersection: http://ruby-doc.org/core-2.2.0/Array.html#method-i-26
The manual for difference: http://ruby-doc.org/core-2.2.0/Array.html#method-i-2D
You could just do this:
array2 + (array1 - array2)
my_array = ['user1', 'user2', 'user3']
my_array2 = ['user3']
my_array2 + (my_array.sort - my_array2)

Using a ternary statement in Rails 4 in a query using group_by

I ran into something interesting that I think would be related to operator precedence, but not sure if I'm just leaving something out. I would like to use a ternary statement in my .group_by sort on a DB query in Rails. So I have something like this that works:
#tools = Tool.all.group_by {|tool| tool.name}
#=> #tools {'anvil' => [<#tool....
which returns a hash tool objects, grouped into keys where the name is the same. It was then brought up that to just sort them into alphabetical groups by first letter of the name would be the desired output so:
#tools = Tool.all.group_by {|tool| tool.name.downcase[0] }
#=> #tools {'a' => [<#tool.....
So great, now I have a hash of the tools grouped by the first letter of their name. But what if a name starts with a number of something else? Not a problem, it really just pulls the first character and uses that for the group, so tool names starting with "1" get sorted into the hash member whose key is "1". Same for any non-number characters that aren't letters.
Here's the question: I can use a conditional statement to choose to sort all of my alphabetical names into letter groups, but put everything else into a single group with some key like "#". But I can't do it with a ternary statement:
#tools = Tool.all.group_by {|tool| if ('a'..'z').include? tool.name.downcase[0] then tool.name.downcase[0] else '#' end }
works great! I get all of my non-letter names sorted into the #tools['#'] part of the hash.
But this does not work:
#tools = Tool.all.group_by {|tool| ('a'..'z').include tool.name.downcase[0] ? tool.name.downcase[0] : '#' }
It returns a hash with only two members: #tools[true] and #tools[false]. I can kind of see why, as a ternary operator is returning true or false, but shouldn't it act like the if-then-else statement? It has to be something with the group_by that is jumping the gun?
Is there some way to tweak the syntax of the group_by statement to make the ternary operator work like I want it to? I have tried enclosing the two return statements in parens () but that didn't seem to work. I tried the entire ternary statement in parens hoping it would eval the whole thing before returning to the group_by function... any ideas?
This is being parsed by ruby as
('a'..'z').include?(tool.name.downcase[0] ? tool.name.downcase[0] : tool.name = '#')
which is the same as ('a'..'z').include?(tool.name.downcase[0]), assuming none of the names are empty?. For it to be equivalent to your previous version you'd need
('a'..'z').include?(tool.name.downcase[0]) ? tool.name.downcase[0] : tool.name = '#'
As an aside, actually changing the name with tool.name='#' sounds like a really bad idea to me. It might not matter here but could easily bite you later on.

Elegantly extracting all strings from an arbitrarily deep hash

My goal is to determine whether there is a blank in a hash like this:
{"departure_time_slots"=>{"date"=>[""], "time"=>{"start"=>[""], "end"=>[""]}}}
The strings are nested arbitrary times. I do not want to check each of them manually. It would be nice if I can extract all the strings regardless of their depth. Is there a simple way to do that?
Here's an example of how you can extract the values. However you will still have the problem of matching the values to the keys. You can't really have both at the same time.
# Extend the Hash class
class Hash
def recursive_values
self.values.collect { |v|
v.is_a?(Hash) ? v.recursive_values : v
}.flatten
end
end
Usage:
h = {"departure_time_slots"=>{"date"=>[""], "time"=>{"start"=>[""], "end"=>[""]}}}
h.recursive_values
=> ["", "", ""]
It will be better if you will use sth like that:
departure_time_slots = {:date => Time.new, :time_start => nil, :time_end => nil}
And when you use keys in Hash it is good practise to using Symbols for keys. (http://www.ruby-doc.org/core-1.9.3/Symbol.html)
No not possible. Because they are totally present in different scope with respect to each other.
For e.g.. keys start and end is totally unknown and masked from the departure_time_slots object in the example above.
One round abut way could be, getting all the values of the hashmap which are of type hashmap again and obtaining their keys recusively.
Fetch keys of departure_time_slots and then from the value list of that map, find all the keys from every value, if that were to be a hashmap. Other than that, I don't think there is another way.
P.S. On side note, see if u can modify your structure to an array where elements can also be arrays, and try and use flatten concept of arrays. :P

Creating a simple array with multiple values in Ruby on Rails

Arrays have always been my downfall in every language I've worked with, but I'm in a situation where I really need to create a dynamic array of multiple items in Rails (note - none of these are related to a model).
Briefly, each element of the array should hold 3 values - a word, it's language, and a translation into English. For example, here's what I'd like to do:
myArray = Array.new
And then I'd like to push some values to the array (note - the actual content is taken from elsewhere - although not a model - and will need to be added via a loop, rather than hard coded as it is here):
myArray[0] = [["bonjour"], ["French"], ["hello"]]
myArray[1] = [["goddag"], ["Danish"], ["good day"]]
myArray[2] = [["Stuhl"], ["German"], ["chair"]]
I would like to create a loop to list each of the items on a single line, something like this:
<ul>
<li>bonjour is French for hello</li>
<li>goddag is Danish for good day</li>
<li>Stuhl is German for chair</li>
</ul>
However, I'm struggling to (a) work out how to push multiple values to a single array element and (b) how I would loop through and display the results.
Unfortunately, I'm not getting very far at all. I can't seem to work out how to push multiple values to a single array element (what normally happens is that the [] brackets get included in the output, which I obviously don't want - so it's possibly a notation error).
Should I be using a hash instead?
At the moment, I have three separate arrays, which is what I've always done, but I don't particularly like - that is, one array to hold the original word, one array to hold the language, and a final array to hold the translation. While it works, I'm sure this is a better approach - if I could work it out!
Thanks!
Ok, let's say you have the words you'd like in a CSV file:
# words.csv
bonjour,French,hello
goddag,Danish,good day
stuhl,German,chair
Now in our program we can do the following:
words = []
File.open('words.csv').each do |line|
# chomp removes the newline at the end of the line
# split(',') will split the line on commas and return an array of the values
# We then push the array of values onto our words array
words.push(line.chomp.split(','))
end
After this code is executed, the words array had three items in it, each item is an array that is based off of our file.
words[0] # => ["bonjour", "French", "hello"]
words[1] # => ["goddag", "Danish", "good day"]
words[2] # => ["stuhl", "German", "chair"]
Now we want to display these items.
puts "<ul>"
words.each do |word|
# word is an array, word[0], word[1] and word[2] are available
puts "<li>#{word[0]} is #{word[1]} for #{word[2]}</li>"
end
puts "</ul>"
This gives the following output:
<ul>
<li>bonjour is French for hello</li>
<li>goddag is Danish for good day</li>
<li>stuhl is German for chair</li>
</ul>
Also, you didn't ask about it, but you can access part of a given array by using the following:
words[0][1] # => "French"
This is telling ruby that you want to look at the first (Ruby arrays are zero based) element of the words array. Ruby finds that element (["bonjour", "French", "hello"]) and sees that it's also an array. You then asked for the second item ([1]) of that array and Ruby returns the string "French".
You mean something like this?
myArray.map{|s|"<li>#{[s[0],'is',s[1],'for',s[2]].join(" ")}</li>"}
Thanks for your help guys! I managed to figure a solution out based on your advice
For the benefit of anyone else who stumbles across this problem, here's my elided code. NB: I use three variables called text, language and translation, but I suppose you could replace these with a single array with three separate elements, as Jason suggests above.
In the Controller (content is being added via a loop):
#loop start
my_array.push(["#{text}", "#{language}", "#{translation}"])
#loop end
In the View:
<ul>
<% my_array.each do |item| %>
<li><%= item[0] # 0 is the original text %> is
<%= item[1] # 1 is the language %> for
<%= item[2] # 2 is the translation %></li>
<% end %>
</ul>
Thanks again!

Resources