taglist = [{:name=>"Daniel_Xu_Forever", :tag=>["helo", "world"]},
{:name=>"kcuf", :tag=>["hhe"]},
{:name=>"fine", :tag=>[]},
{:name=>"how hare you", :tag=>[]},
{:name=>"heki", :tag=>["1", "2", "3"]},
{:name=>"railsgirls", :tag=>[]},
{:name=>"_byoy", :tag=>[]},
{:name=>"ajha", :tag=>[]},
{:name=>"nimei", :tag=>[]}]
How to get specified name's tag from taglist
For example , I want to extract user "fine"'s tag?
Could this be achieved without do iterator?
This will return the contents of the :tag key for any users name which == 'fine'
taglist.select { |x| x[:name] == 'fine' }.map { |u| u[:tag] }
First you select out only the users you are interested with .select.
And then use .map to collect an array of only what you want.
In this case the end result will be: []
Is do really an iterator?
taglist.find{|tl| tl[:name] == 'fine'}[:tag]
Just to be silly how about:
eval taglist.to_s[/:name=>"fine", :tag=>(.*?)}/, 1]
#=> []
No, it cannot be done without a loop.
And even if you find a solution where your code avoids a loop, for sure the library function that you're calling will include a loop. Finding an element in an array requires a loop. Period.
For example, take this (contrived) example
pattern = "fine"
def pattern.===(h); self == h[:name]; end
taglist.grep(pattern)
which does not seem to use a loop, but calls grep which is implemented using a loop.
Or another, equally contrived, example
class Hash; def method_missing(sym); self[sym]; end; end
taglist.group_by(&:name)["fine"]
which again does seem to call group_by without a loop, but actually it does.
So the answer is, no.
So my first answer missed the no do rule.
Here is an answer that doesn't use a do block.
i=0
begin
if taglist[i][:name] == 'fine'
tag = taglist[i][:tag]
break
end
i+=1
end while i < taglist.length - 0
Technically I think this is still using a block. But probably satisfies the restriction.
Related
I'm trying to reduce the while loop below to a single line
def this_method(week)
i = 0
while i < array.length
yield(week[i])
i += 1
end
end
week.each do |week|
puts week
end
Like others, I'm confused about the example (array is not defined, and this_method is never called). But you certainly don't need the while loop. I'd just use the Integer#times method, since you're making no use of the array values:
array.length.times {|i| yield week[i]}
#each_index (which ram suggested) works just as well.
But if array is actually meant to be week, then it gets even simpler:
week.each {|x| yield x}
I'm not sure why you'd want to create a method that just recycles #each though.
For since line you can use Array#each_index:
array.each_index {|i| yield week[i] }
No, you can't. The ternary operator is a conditional expression, the while is a loop expression.
However, in Ruby you normally use enumerators, not while. Your code can be rewritten as
def this_method(week)
array.each_with_index { |item, i| yield(week[i]) }
end
What is not clear to me, is there the array variable comes from. Even in your example, there is no definition of such variable.
if in any form check conditions only once.
while on other hand, can check conditions many times.
Well, if you don't like other answers with enumerators you can use while in a different form:
def this_method(week)
i = -1
yield(week[i]) while (i+=1) < array.length
end
1.I can't find an elegant way to write this code:
if array.empty?
# process empty array
else
array.each do |el|
# process el
end
end
I'd like to have one loop, without writing array twice. I read this, but there is no solution good enough.
2.
I am actually in an HAML template. Same question.
- if array.empty?
%p No result
- else
%ul
- array.each do |el|
%li el
What about?
array.each do |x|
#...
puts "x",x
end.empty? and begin
puts "empty!"
end
The cleanest way I've seen this done in HAML (not plain Ruby) is something like:
- array.each do |item|
%li
= item.name
- if array.empty?
%li.empty
Nothing here.
As mentioned by other answers, there is no need for the else clause because that's already implied in the other logic.
Even if you could do the each-else in one clean line, you wouldn't be able to achieve the markup you're trying to achieve (<p> if array.empty?, <ul> if array.present?). Besides, the HAML you show in your question is the best way to tell the story behind your code, which means it will be more readable and maintainable to other developers, so I don't know why you would want to refactor into something more cryptic.
I think there is no much more elegant or readable way to write this. Any way to somehow combine an iteration with a condition will just result in blackboxed code, meaning: the condition will just most likely be hidden in an Array extension.
If array is empty, then it will not be iterated, so the each block does not need to be conditioned. Since the return value of each is the receiver, you can put the each block within the empty? condition.
if (array.each do |el|
# process el
end).empty?
# process empty array
end
Assuming that "process empty array" leaves it empty after processing, you can leave out the else:
if array.empty?
# process empty array
end
array.each do |el|
# process el
end
or in one line:
array.empty? ? process_empty_array : array.each { |el| process_el }
An if the array is nil then we can enforce to empty array
if (array || []).each do |x|
#...
puts "x",x
end.empty?
puts "empty!"
end
I saw some people asking how to handle this for nil cases.
The trick is to convert it to string. All nils converted to string becomes a empty string, all empty cases continue being empty.
nil.to_s.empty?
"".to_s.empty?
both will return true
Is there a keyword I can use to explicitly tell the map function what the result of that particular iteration should be?
Consider:
a = [1,2,3,4,5]
a.map do |element|
element.to_s
end
In the above example element.to_s is implicitly the result of each iteration.
There are some situations where I don't want to rely on using the last executed line as the result, I would prefer to explicitly say what the result is in code.
For example,
a = [1,2,3,4,5]
a.map do |element|
if some_condition
element.to_s
else
element.to_f
end
end
Might be easier for me to read if it was written like:
a = [1,2,3,4,5]
a.map do |element|
if some_condition
result_is element.to_s
else
result_is element.to_f
end
end
So is there a keyword I can use in place of result_is?
return will return from the calling function, and break will stop the iteration early, so neither of those is what I'm looking for.
The last thing left on the stack is automatically the result of a block being called. You're correct that return would not have the desired effect here, but overlook another possibility: Declaring a separate function to evaluate the entries.
For example, a reworking of your code:
def function(element)
if (some_condition)
return element.to_s
end
element.to_f
end
a.map do |element|
function(element)
end
There is a nominal amount of overhead on calling the function, but on small lists it should not be an issue. If this is highly performance sensitive, you will want to do it the hard way.
Yes, there is, it's called next. However, using next in this particular case will not improve readability. On the contrary, it will a) confuse the reader and b) give him the impression that the author of that code doesn't understand Ruby.
The fact that everything is an expression in Ruby (there are no statements) and that every expression evaluates to the value of the last sub-expression in that expression are fundamental Ruby knowledge.
Just like return, next should only be used when you want to "return" from the middle of a block. Usually, you only use it as a guard clause.
The nature of map is to assign the last executed line to the array. Your last example is very similar to the following, which follows the expected behavior:
a = [1,2,3,4,5]
a.map do |element|
result = if some_condition
element.to_s
else
element.to_f
end
result
end
No, there is no language keyword in ruby you can use to determine the result mapped into the resulting array before executing other code within the iteration.
You may assign a variable which you then return when some other code has been executed:
a.map do |element|
result = some_condition ? element.to_s : element.to_f
#do something else with element
result
end
Keep in mind the reason for ruby not providing a keyword for this kind of code is that these patterns tend to have a really low readability.
I need to iterate over an array, excluding a value in the middle. I know I can do
(0..10).each do |i|
unless i==6
...
end
end
But I'm wondering if there's a cleaner way. The closest solution I found was this:
Is there an elegant way to exclude the first value of a range?
I need to do this to iterate through poorly organized data I was given.
((0..10).to_a - [6]).each do |i|
...
end
You can make usage of reject method to accomplish it.
I don't know if its a cleaner way but it is just another way.
(0..10).reject { |v| v == 6 }.each do |i|
Give a shot and tell me what do you think about it. :)
Not sure is it cleaner or preferred way for your simple case, but you can do something like
(0..10).select{|i| i!=6}.each do
# YOUR CODE
end
in more complex cases this is definitely a way
P.S. while writing the answer, the answer with reject one arrived :) this is almost the same one, i just forgot of reject, remembered only select :)
You can make use of next if you just want to get around wrapping your code in an unless block. For example, if you want to skip a set of numbers, you can do something like:
skip_values = [3, 6, 7]
(0..10).each do |i|
# When i is 3, 6, or 7, continue to the next iteration
next if skip_values.include?(i)
...
end
I suppose you could define a subclass or patch Range itself with something like...
def reach not_this_one
each do |x|
yield x unless x == not_this_one
end
end
...or perhaps...
def reach not_this_one, &block
self.select { |x| x != not_this_one }.each &block
end
You can temporarily exclude a value by using Enumerable#grep_v:
(1..5).grep_v(2).each { |n| puts n }
#1
#3
#4
#5
Unfortunately grep_v like some of the other posted answers creates an intermediate array. To be honest I prefer your initial approach, which can be written more neatly (without monkey-patching) as:
(1..5).each { |n| puts n unless n == 2 }
#1
#3
#4
#5
basically I am trying to write an if condition that looks at all the contents of an array to judge whether that condition is true.
Basically, I want to do:
SubScale.all.each do |ss|
if ss.key IN(scales)
execute this code
end
end
Where scales in an array and I want the code to be executed if ss.key is any entry in that array.
You're looking for Array#include? :
scales.include?(ss.key)
More general than Array#include?—which requires you to check by value—is Enumerable#any?:
SubScale.all.each do |ss|
# Run the code if the value is an exact match
run_code if scales.include?(ss.key)
end
SubScale.all.each do |ss|
# Run the code if the block returns a truthy value
run_code if scales.any?{ |scale| scale.downcase == ss.key.downcase }
end
Finally, if it is an exact match you want, and speed turns out to be an issue (profile first!), you can trade memory for performance by using a hash to look up your key in O(1) instead of O(n) time:
scale_lookup = Hash[ scales.map{ |s| [s,true] } ]
SubScale.all.each do |ss|
run_code if scale_lookup[ss.key]
end
scales.include?(ss.key) is what you need.
If scales has many elements and so does SubScale.all I would suggest creating temporary set (or hash):
require "set"
scales_set = Set.new(scales)
....each do |ss|
if scales_set.include?(ss.key)
...
end
end
This could be much faster.
P.S. Hash seems to be faster than set:
scales_hash = scales.inject({}) { |h, e| h[e] = true; h }
....each do |ss|
if scales_hash.has_key?(ss.key)
...
end
end