Compare 2 instance objects and find uncommon elements - ruby-on-rails

I have 2 instance objects : #original_files and #version2_files
I want to retrieve the elements found in #original_files but NOT FOUND in #version2_files.
The code below:
<% #original_files.each do |original_file| %>
<% #version2_files.each do |ver2_file| %>
<% if original_file.name == ver2_file.name%>
<%= original_file.name %> is common to both sets
<% break %>
<% else %>
<% end %>
<% end %>
<% end %>
I would appreciate it if someone could help me figure out how to find the elements found in #original_files only.
Thanks

They are arrays? You can subtract...
#original_files - #version2_files
Hashes can use diff.
#original_files.diff(#version2_files)

I understand this to mean that #original_files and #version2_files are hashes, and you want to find elements in #original_files that are not in #version2_files
#original_files.keys - #version2_files.keys

disclaimer: new to ruby and may be ignorant of how hashes/arrays work exactly.
#danh's answer is right if you want to compare on key/value pairs. But if you're comparing the 2 hashes based on their values only and the keys are not important, then diff may not be what you need. In which case, you would do something like:
original.values - version2.values => [ ... ]
which returns an array of elements found only in original but not version2.
Also, this and this may help.

Related

How do I elegantly check for presence of both the object and associated objects?

I have an instance variable #tally_property, and if there are photos on that object I would like to cycle through the photos and show them.
So my code snippet looks like this:
<% if #tally_property.photos.present? %>
<% #tally_property.photos.each_with_index do |photo, index| %>
The issue is that based on the above, if #tally_property is nil, then the entire first line throws an error.
So is there a 'nil' check I can do that isn't bulky, i.e. I don't want to do if #tally_property.nil?, on both the primary object and the association, and is elegant and ruby & rails-esque?
I would use the safe navigation operator (&.) and write something like this:
<% #tally_property&.photos&.each_with_index do |photo, index| %>
...
<% end %>
In Ruby 2.3.0+ you can use the safe navigation operator:
#tally_property&.photos
ActiveSupport has a .try method that can be used to the same end in older versions of ruby:
#tally_property.try(:photos)
You can add a simple conditional to be able to safely iterate through the collection:
<% (#tally_property.try(:photos)||[]).each_with_index do |photo, index| %>
<% end %>
Rails 4 adds ActiveRecord::Relation#none and a change in behaviour so that associations always return a ActiveRecord::Relation. So its perfectly acceptable to write:
<% #tally_property.try(:photos).try(:each_with_index) do |photo, index| %>
<% end %>
After upgrading your app. Or you can use a partial and render:
<%= render partial: 'photos', collection: #tally_property.photos if #tally_property %>
Which removes the need for writing the iteration.
Use && (or and, they each have their sweetspot).
Taking it out of Erb for a moment, I would generally write something like this:
if #tally_property and #tally_property.photos.present?
Depending on photos I might use:
if #tally_property and #tally_property.photos
or perhaps:
if #tally_property and not #tally_property.photos.empty?
Sometimes I'll use a temporary variable:
if (photos = #tally_property && #tally_property.photos)
photos.each #…
That kind of thing.
I would recommend this episode of Ruby Tapas, And/Or for a longer (but still quick) look at it.
One more way, just select all photos connected to this tally_property:
example how it might be:
Photo.joins(:tally_property).each_with_index do |photo, index|

How do I group objects returned by a REST API based on a value inside that object?

I'm pretty new to ruby/rails so bear with me.
I'm attempting to take the results returned by the JIRA rest API and render them in a view. I can do that pretty easily using the jira-ruby gem. The problem I'm having is grouping the results by a specific object inside the object returned by the API (in this case, a "components" field object inside of a "issue" object). I've attempted to use group_by and chunk for this but I'm basically getting the inverse of what I want. Both methods return the same result.
In my controller I have:
#issues = #jira_client.Issue.all
In my view I have:
<% #issues.chunk {|issue_comp| issue_comp.fields["components"]}.each do |comps, issues| %>
<h2>
<% comps.each do |comp| %>
<%= comp["name"] %>
<% end %>
</h2>
<ul>
<% issues.each do |issue| %>
<li><p><%= link_to issue.key, "http://localhost:2990/jira/browse/#{issue.key}" %> - <%= issue.summary %></p></li>
<% end %>
</ul>
<% end %>
What I end up with is:
CompA CompB
IssueA
CompC CompD
IssueB
CompA CompC CompD
IssueC
etc.
What I want is:
CompA
IssueA
IssueC
CompB
IssueA
CompC
IssueB
IssueC
CompD
IssueB
IssueC
The object returned by the API is a pretty convoluted object (i.e. giant array of hashes inside arrays inside of hashes). So, I have to dig pretty deep to get at the component name.
I get the feeling that this shouldn't be as complicated as it seems but I have a terrible habit of making things more complicated than they need to be. What am I doing wrong here?
EDIT: I created a gist of the full dump that is returned with the above call. Notice the "components" array:
jira-ruby gem dump for all issues
I took a look at the data you're getting back from Jira. This is how it looks to me:
There is an outer array of Jira Issues.
Each issue has an "attrs" hash
Each "attrs" hash contains components.
If this understanding is correct, I think you are attempting to invert that structure so that you can get a complete list of components, then iterate over each of them, and show the Issues that belong to that component.
If that understanding is correct, you have two basic choices:
Check if you can ask Jira for that information (so you don't have to generate it yourself), or
Build your own data structure (in memory on in a local DB as you prefer):
Some sample code for building a useful structure in-memory:
# in a controller, model, or service class (as you wish)
#components = {}
#jira_issues_array.each do |jira_issue| # from your API call
jira_issues[:components].each do |jira_component|
#components[jira_component[:key]] ||= { name: jira_component[:name], issue_keys: [] }
#components[jira_component[:key]][:issue_keys] << jira_issue[:key]
end
end
In your view, you could iterate over #components like this:
# some html.erb file:
<h1>Components and Issues</h1>
<ul>
<% #components.keys.each do |component_key, component| %>
<li><%= component[:name] %>
<ul> <!-- nested -->
<% component[:issue_keys].each do |issue_key| %>
<%= #jira_issues_array.find { |issue| issue[:key] == issue_key }[:name] %>
<% end %>
</ul>
</li>
<% end %>
</ul>
Note: Like a typical lazy programmer, I haven't tried this out, but it's really intended to show how you might go about it. For example, each issue's name is embedded in the attrs section, so you'll need to dig that out a bit.
Finally, if anyone would find this useful, I use this to analyse and reformat JSON.
HTH - any questions or problems, post a comment.

Ruby on Rails turn each iteration into an array

I feel like this is such an easy question
<% #state.cities.each do |city| %>
<%= city.id %>
<% end %>
puts the ids as follows:
1
2
3 etc...
How do I turn the iteration into an array?
so it outputs as follows:
[1,2,3,4,etc...]
There is a method that does just that!
What you are looking for is the map method.
Creates a new array containing the values returned by the block.
http://apidock.com/ruby/Array/map
The documentation states, creates an array containing the values returned by a block.
#state.map do |state|
state.id
end
=> [1,2,3,...]
Which is the same as:
#state.map(&:id)
=> [1,2,3,...]
But uses the Ruby Enumerable shorthand.
#state.map(&:id) would give you the same result!
You can use map:
<%= #state.map(&:id) %>

How to add "0," after each loop in Ruby

How to add "0," after each loop in Ruby (At the data: part of the series)
So first loop would
show only the value
next loop 0,value
next loop 0,0,value
next loop 0,0,0,value
etc..
series: [
<% #prot = ProjectTask.where("project_id = ? AND taskType = ?" ,#project.id, "Pre-Sales")%>
<% #prot.each do |prt| %>
<% hoursSum = 0 %>
{
name: '<%= prt.task_name%>',
data: [(here after each loop of #prot i want to add "0," here)<% #taskHours = Effort.where(:project_task_id => prt.id) %>
<% #taskHours.each do |th| %>
<% hoursSum = hoursSum + th.hours %>
<% end %>
<%= hoursSum%>
]
},<% end %>
<% #prot.each_with_index do |index, prt| %>
...
<%= "0," * index %>
...
<% end %>
This logic seems too complex to be in a view - I would migrate it to a helper function that builds up the data array and call that instead.
I think you'll find it a lot easier to do what you want then without having to deal with all the clutter of the erb tags etc. There's lots of ways you could do it - Yossi's suggestion of using each_with_index is a perfectly good one.
Two little things though - I would advise against shortened names for stuff like #prot - just call it #project_task. It's more readable and you can guarantee you call it the same thing throughout your code (instead of having some places where you call it #prot, others where it's #ptask etc) which will save you more time than you lose typing a longer name, I promise you.
Also - you use camelCase for some of your variables - I would advise sticking with the Ruby convention of snake_case.

Rails: display #cars as a comma-separated list

Based on this query:
#cars = Car.where("manufacturer_id IN ?", #mfts.select("id")).limit(30).select("id")
How can I display the cars' IDs in the view like this (or do I need to rewrite my query)?
3,2,5,12,15,24,34,63,64,65,66,85
Thanks a lot - I've looked for this but couldn't find the right question/answer.
One solution is to do:
#view
<% #cars.each do |c| %><%= c.id %>,<% end %>
I don't know if there's a better way to go about it - this obviously leaves a stray comma at the end of the list (which isn't a dealbreaker). Any more elegant solutions?
One line:
<%= #cars.map(&:id).join(",") %>
If writing &:id seems confusing, there's another way that's a little more readable.. If y'all want to access a method or attribute, it might look better to inline a block.
<%= #cars.map { |car| car.id }.join(", ") %>
P.S... another name for map is collect.. that's what it's called in Smalltalk.
Lookin' good!
With Rails 3.0+ you can now write:
<%= #cars.map { |car| car.id }.to_sentence %>
Rails will appropriately add the comments and the word 'and' between the last two elements.

Resources