Remove duplicates from an array of array - ruby-on-rails

How do I remove duplicates from this array?
product_areas = [["1", "2", "3"], ["3", "1", "2"]]
I have tried product_areas.uniq!, product_area.uniq but the same thing is repeating. What am I missing here?
Expected Output:
product_areas = ["1", "2", "3"]

Try this:
product_areas = [["1", "2", "3"], ["3", "1", "2"]].flatten.uniq
Using flatten on your array will create the following result:
["1", "2", "3", "3", "1", "2"]
When you call uniq on that array, you will get the result you were expecting:
["1", "2", "3"]

As previously pointed out
product_areas = [["1", "2", "3"], ["3", "1", "2"]].flatten.uniq
-OR-
product_areas.flatten.uniq!
will both lead you to your desired answer.
Why?
When you were running "product_areas.uniq!" the process was comparing the two inner arrays against each other, other than the elements of each array. Because both ["1", "2", "3"] and ["3", "1", "2"] are unique in the array, neither will be removed. As an example say you had the following array
product_areas = [["1", "2", "3"], ["3", "1", "2"], ["1","2","3"]]
and you ran:
product_areas = product_areas.uniq
product_areas would then look like the following:
product_areas = [["1", "2", "3"], ["3", "1", "2"]]
What you need to be aware of when running any sort of enumerable method on arrays is it will only move down to each individual element. So if inside an array you have more arrays, any iterative method will look at the inner array as a whole. Some sample code to demonstrate this:
array_of_arrays = [[1,2,3], [4,5,6]]
array_of_arrays.each do |array|
p array
end
#---OUPUT---
# [1, 2, 3]
# [4, 5, 6]
array_of_arrays.each do |array|
array.each do |element|
p element
end
end
#---OUPUT---
# 1
# 2
# 3
# 4
# 5
# 6

I've used this little snippet of code over and over again in my career as a Ruby on Rails developer to solve this frequently encountered problem in a single piece of neat little code. The end result of this code is that you can just call something like
product_areas.squish
to do the following:
flatten 2-D arrays into 1-D arrays
remove nil elements
remove duplicate elements
I do this by adding an initializer config/initializer/core_ext.rb to my project which extends the core Ruby functionality as follows:
class Array
def squish
blank? ? [] : flatten.compact.uniq
end
end
class NilClass
def squish
[]
end
end

Related

How to change hash key from array of hashes in ruby?

Given:
data = [
{"votable_id"=>1150, "user_ids"=>"1,2,3,4,5,6,"},
{"votable_id"=>1151, "user_ids"=>"55,66,34,23,56,7,8"}
]
This is the expected result. Array should have first 5 elements.
data = [
{"votable_id"=>1150, "user_ids"=>["1","2","3","4","5"]},
{"votable_id"=>1151, "user_ids"=>["55","66","34","23","56","7",8"]}
]
This is what I tried :
data.map{|x| x['user_ids'] = x['user_ids'].split(',').first(5)}
Any other optimized solution ?
You can also use .map and .tap like this
data.map do |h|
h.tap { |m_h| m_h["user_ids"]= m_h["user_ids"].split(',').first(5)}
end
data = [
{"votable_id"=>1150, "user_ids"=>"1,2,3,4,5,6,"},
{"votable_id"=>1151, "user_ids"=>"55,66,34,23,56,7,8"}
]
Code
h=data.map do |h|
h["user_ids"]=[h["user_ids"].split(',').first(5)].flatten
h
end
p h
Output
[{"votable_id"=>1150, "user_ids"=>["1", "2", "3", "4", "5"]}, {"votable_id"=>1151, "user_ids"=>["55", "66", "34", "23", "56"]}]
data.map { |h| h.merge("user_ids"=>h["user_ids"].split(',').first(5)) }
#=> [{"votable_id"=>1150, "user_ids"=>["1", "2", "3", "4", "5"]},
# {"votable_id"=>1151, "user_ids"=>["55", "66", "34", "23", "56"]}]
See Hash#merge. This leaves data unchanged. To modify (or mutate) data use Hash#merge! (aka update). h.merge(k=>v) is a shortcut for h.merge({ k=>v }).

Iterate hash of arrays

{"title"=>["111", "222", "333"], "rating"=>["1", "2", "3"], "reviews"=>["11", "22", "33"]}
I have data with me from the form in the above format.
Now I want to iterate through this loop so that I can get data in below sequence as per my requirement:
["111", "1", "11"], ["222", "2", "22"], ["333", "3", "33"]
Actually I have to save each record in my database in the way so that a title, a rating and a review forms one row for my database table.
I have tried many possible solutions with 'each_with_index', key, value hash way, but no luck.
Thanks in advance.
Assuming every value in Hash is an Array of same length, transpose array hash.values :
hash = {"title"=>["111", "222", "333"], "rating"=>["1", "2", "3"], "reviews"=>["11", "22", "33"]}
hash.values
#=> [["111", "222", "333"], ["1", "2", "3"], ["11", "22", "33"]]
hash.values.transpose
#=> [["111", "1", "11"], ["222", "2", "22"], ["333", "3", "33"]]
While the transpose solution is very nice, it will error out when not all value arrays have the same number of elements. This will still work (replacing missing elements with nil):
h.values.reduce(:zip).map(&:flatten)
#=> [["111", "1", "11"], ["222", "2", "22"], ["333", "3", "33"]]
If you know that your input is well-formed, go for the transpose option.

How to add elements of an array to another array and remove duplicates

I'm trying to create a json file in ruby on rails that has a specific format like so:
{"nodes":["1","2","3"],"edges":[["1","3"],["2","3"],["2","1"]]}
I have a method in my model that is as follows:
def self.joinup(id)
c = Challenge.find(id)
result={}
user_ids = c.users.pluck(:id)
result["nodes"] = user_ids.collect(&:to_s)
result["edges"] = Relationship.where(follower_id: user_ids).map{|h| [h.follower_id.to_s, h.followed_id.to_s]}
result["nodes"] = result["nodes"]|result["edges"]
result
end
and this produces:
{"nodes":["1","2","3","3","3"["4","5"],["5","6"]],"edges":[["4","5"],["5","6"]]}
whereas was I want is:
{"nodes":["1","2","3","4","5","6"],"edges":[["4","5"],["5","6"]]}
In rails you can use the array method flatten which returns an array with one dimension, like this
array = ["1", "2", "3", ["2", "3", "4"], "5"]
array.flatten #=> ["1", "2", "3", "2", "3", "4", "5"]
After that you can use the method uniq which returns all the different values inside your array, so this will return this as a result
array.flatten.uniq #=> ["1", "2", "3", "4", "5"]
Also, this method can be used with the bang (!) operator, which change the original array with the return values, so it will be like this
array.flatten!.uniq!
Hope it helps :D
You merged the contents in result["edges"] in this line:
result["nodes"] = result["nodes"]|result["edges"]
after that, you may firstly flatten the array and then uniq:
result["nodes"].flatten!.uniq!
result
You can use flatten with uniq:
result["nodes"] = (result["nodes"] | result["edges"].flatten).uniq

Create array of arrays from database query in rails

Ive got a table which consists of two fields called follower_id and followed_id. I need to create a query which creates an array of from each row and puts that in an overall array so that the end structure looks like:
"edges": [
["1", "2"],
["1", "3"],
["3", "4"],
["3", "5"]
]
so far I have
def self.including_relationships
result={}
result["edges"] Relationship.all.each do |relationship|
result[""]= Relationship.select(:follower_id.to_s,:follower_id.to_s)
#the code here is called once for each user
# user is accessible by 'user' variable
end
result
end
but this produces:
edges: [
"[4, 3, 3, 4]",
"[3, 4, 3, 4]"
]
You can use map to build an array like:
Relationship.all.map { |r| [r.follower_id.to_s, r.followed_id.to_s] }
Try this
relationships = Relationship.all.map { |r| [r.follower_id.to_s, followed_id.to_s] }
results = {"edges": relationships }

reading array of params in RoR

If I have URL something like below:
http://test.com?x=1&x=2&x=3&x=4&x=5&x=6&x=7
Then how can I read all "x" values?
Added new comment: Thanks for all your answers. I am basically from Java and .Net background and recently started looking Ruby and Rails. Like in Java, don't we have something similar as request.getParameterValues("x");
You should use following url instead of yours:
http://test.com?x[]=1&x[]=2
and then you'll get these params as array:
p params[:x] # => ["1", "2"]
IF IT IS A STRING, but not a http request
Do this:
url = 'http://test.com?x=1&x=2&x=3&x=4&x=5&x=6&x=7'
p url.split(/(?:\A.*?|&)x=/).drop(1)
If you want to convert them to integers, do:
p url.split(/(?:\A.*?|&)x=/).drop(1).map(&:to_i) (ruby 1.9)
or
p url.split(/(?:\A.*?|&)x=/).drop(1).map{|v| v.to_i}
IF IT IS A STRING, but not a http request I even didn't imagine if author don't know how to handle params of request url...
url = "http://test.com?x=1&x=2&x=3&x=4&x=5&x=6&x=7"
vars = url.scan(/[^?](x)=([^&]*)/)
#=> [["x", "2"], ["x", "3"], ["x", "4"], ["x", "5"], ["x", "6"], ["x", "7"]]
x = vars.map{|a| a[1]}
#=> ["2", "3", "4", "5", "6", "7"]
x.map!(&:to_i)
#=> [2, 3, 4, 5, 6, 7]
Or if you need to extract valuse only:
vars = url.scan(/[^?]x=([^&]*)/).flatten
#=> ["2", "3", "4", "5", "6", "7"]
vars = url.scan(/[^?]x=([^&]*)/).flatten.map(&:to_i)
#=> [2, 3, 4, 5, 6, 7]

Resources