接受参数作为单个对象或对象数组

时间:2022-11-12 16:39:26

I have a bunch of functions that take in either an array of objects, or a single object designed to be treated as an array containing one object, and I am looking for a cleaner way to accomplish this. Basically, I want to know how I could make the unless part in the following function more concise:

我有一堆函数接受一个对象数组,或者一个对象被设计为一个包含一个对象的数组,我正在寻找一种更简洁的方法来完成它。基本上,我想知道如何使除非以下函数中的部分更简洁:

def foo(bar_or_bars)
  unless bar_or_bars.is_a?(Array)
    bar_or_bars = [bar_or_bars]
  end
  bar_or_bars.each { |baz| ... }
end

Any help will be appreciated! Thanks.

任何帮助将不胜感激!谢谢。

3 个解决方案

#1


13  

First thing you could do is to write the unless logic in a single line:

你要做的第一件事就是在一行中写出除非逻辑:

bars = bar_or_bars.is_a?(Array) ? bar_or_bars : [bar_or_bars]

As you see, I give it a new name here, as it's no longer a bar or bars, it's now definitely a collection.

如你所见,我在这里给它一个新名字,因为它不再是一个酒吧或酒吧,它现在绝对是一个集合。

The problem with this and your original approach is that although your function could work on any Enumerable, you will force your users to give you an argument of a specific type, which breaks duck typing.

这个和你原来的方法的问题在于,虽然你的函数可以在任何Enumerable上运行,但是你会强迫你的用户给你一个特定类型的参数,这会破坏鸭子的输入。

A neat trick to partially solve that issue is the following:

部分解决该问题的巧妙方法如下:

def foo(bar_or_bars)
  bars = [*bar_or_bars]
  bars.each { |baz| ... }
end

I wouldn't exactly call that readable, though. It actually smells a lot like bad API design. Probably you should better take multiple arguments like this:

不过,我不会完全称之为可读。它实际上闻起来很像糟糕的API设计。可能你应该更好地采取这样的多个参数:

def foo(*bars)
  bars.each { |baz| ... }
end

And let the caller decide whether he wants to pass a single object or an array:

让调用者决定是否要传递单个对象或数组:

foo("XYZ")
ary = ["abc", "def"]
foo(*ary)

#2


19  

The cleanest solution I have found is using the kernel method Array:

我发现最干净的解决方案是使用内核方法Array:

Array(5) #=> [5]
Array([1, 2, 3]) #=> [1,2,3]

so

所以

def foo(bar_or_bars)
  bars = Array(bar_or_bars)
  bars.each { |baz| ... }

This will even work on nested arrays that have arrays as elements (they wont be flattened out)

这甚至可以在具有数组作为元素的嵌套数组上工作(它们不会被展平)

#3


1  

I am currently using this:

我目前正在使用这个:

bars = [bar_or_bars].flatten

#1


13  

First thing you could do is to write the unless logic in a single line:

你要做的第一件事就是在一行中写出除非逻辑:

bars = bar_or_bars.is_a?(Array) ? bar_or_bars : [bar_or_bars]

As you see, I give it a new name here, as it's no longer a bar or bars, it's now definitely a collection.

如你所见,我在这里给它一个新名字,因为它不再是一个酒吧或酒吧,它现在绝对是一个集合。

The problem with this and your original approach is that although your function could work on any Enumerable, you will force your users to give you an argument of a specific type, which breaks duck typing.

这个和你原来的方法的问题在于,虽然你的函数可以在任何Enumerable上运行,但是你会强迫你的用户给你一个特定类型的参数,这会破坏鸭子的输入。

A neat trick to partially solve that issue is the following:

部分解决该问题的巧妙方法如下:

def foo(bar_or_bars)
  bars = [*bar_or_bars]
  bars.each { |baz| ... }
end

I wouldn't exactly call that readable, though. It actually smells a lot like bad API design. Probably you should better take multiple arguments like this:

不过,我不会完全称之为可读。它实际上闻起来很像糟糕的API设计。可能你应该更好地采取这样的多个参数:

def foo(*bars)
  bars.each { |baz| ... }
end

And let the caller decide whether he wants to pass a single object or an array:

让调用者决定是否要传递单个对象或数组:

foo("XYZ")
ary = ["abc", "def"]
foo(*ary)

#2


19  

The cleanest solution I have found is using the kernel method Array:

我发现最干净的解决方案是使用内核方法Array:

Array(5) #=> [5]
Array([1, 2, 3]) #=> [1,2,3]

so

所以

def foo(bar_or_bars)
  bars = Array(bar_or_bars)
  bars.each { |baz| ... }

This will even work on nested arrays that have arrays as elements (they wont be flattened out)

这甚至可以在具有数组作为元素的嵌套数组上工作(它们不会被展平)

#3


1  

I am currently using this:

我目前正在使用这个:

bars = [bar_or_bars].flatten