Related
I have this code that works well
def self.select_some_elements(some_value)
return elements.select { |element| element.some_value > some_value}
end
This code returns an array of elements class instances/objects. However, I would like to return the array of element.to_s instances/propeties, not the whole class objects, just strings/string properties
Is there a quick way to do it without going extra steps?
Introduction:
So, what you have achieved is a set (array in this case) of objects, which is great. What you have to do now is to transform this set into a different one: namely, replace each object with what that object's method to_s returns. There's a perfect method for that, called map. It goes through all the items, calls a block with each item as an argument and puts in the resulting array what the block has returned:
Open irb console and play around:
>> [1,2,3].map{|x| 1}
=> [1, 1, 1]
>> [1,2,3].map{|x| x+1}
=> [2, 3, 4]
>> [1,2,3].map{|x| "a"}
=> ["a", "a", "a"]
>> [1,2,3].map{|x| x.to_s}
=> ["1", "2", "3"]
>> [1,2,3].map(&:to_s)
=> ["1", "2", "3"]
The last transformation seems to be what you need:
The answer:
Add .map(&:to_s) at the end like this
def self.select_some_elements(some_value)
return elements.select { |element| element.some_value > some_value}.map(&:to_s)
end
.map(&:to_s) is a short version of .map { |element| element.to_s }. And you can read about Arra3#map in the docs
And when you wrap your head around the #map, check out Stefan's answer which shows how select{}.map can be "compressed" into one filter_map call (doing both things: filter, and mapping in one iteration over the set instead of two).
Starting with Ruby 2.7 there's filter_map:
elements = [1, 2, 3, 4, 5]
elements.filter_map { |e| e.to_s if e > 2 }
#=> ["3", "4", "5"]
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 }).
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
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
I know how it is done row-wise
CSV.foreach(filename.csv) do |row|
puts "#{row}"
end
But I am completely lost column wise?
test.csv:
name,surname,no1,no2,no3,date
Raja,Palit,77489,24,84,12/12/2011
Mathew,bargur,77559,25,88,01/12/2011
harin,Roy,77787,24,80,12/12/2012
Soumi,paul,77251,24,88,11/11/2012
Acces by cols:
require 'csv'
csv = CSV.read('test.csv', :headers=>true)
p csv['name'] #=>["Raja", "Mathew", "harin", "Soumi"]
#or even:
t = CSV.table('test.csv')
p t[:no1] #=> [77489, 77559, 77787, 77251]
Note that in the last case the cols are accessed by their symbolized name and that strings are converted to numbers when possible.
This is the solution guys:
CSV.foreach(filename).map { |row| row[0] }
Sorry for posting it in the correct format so late.
Transpose your CSV file. By doing this your rows covert to column and viceversa. Below a simple example of transpose.
irb(main):001:0> [["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]].transpose
=> [["1", "4", "7"], ["2", "5", "8"], ["3", "6", "9"]]
The method 'by_col' provides you output based on your column selection.
test_csv:
name,surname,no1,no2,no3,date
Raja,Palit,77489,24,84,12/12/2011
Mathew,bargur,77559,25,88,01/12/2011
harin,Roy,77787,24,80,12/12/2012
Soumi,paul,77251,24,88,11/11/2012
Code:
CSV.parse(File.open('test_csv'), headers: true).by_col['surname']
Output:
['Palit','bargur','Roy','paul']
Bored so decided to cook up an alternate solution here. Only works (here) if the first row has the max number of columns
columns = {}
rows.first.each_with_index do |col, i|
columns[i] = []
end
rows.each do |row|
row.each_with_index do |cell, i|
columns[i] = columns[i] + [cell]
end
end
Now you should be able to access each column by its index