Shortcut for add unless nil?

Hi,

Is was wondering if there’s a Ruby idiom for adding variables that
should be arrays but that may be nil, for example:

[1] + (a || [ ]) + (b || [ ]) + (c || [ ])

but that’s a bit ugly IMO. I’ve had a look, but didn’t see anything. I
know I could use a fold or something similar, but I was wondering if
there was something in the realms of ||= (i.e. nice, idiomatic shortcut)
for this kind of thing?

Iain

You could do something like this (untested):

[a, b, c, d].compact.reduce(:+)

though whether it is more clear is debatable. I think it reads fine if
you’re fluent.

Sent from my iPhone

El 24/03/2011, a les 19:36, Iain B. [email protected] va
escriure:

On Mar 24, 2011, at 11:36 , Iain B. wrote:

Hi,

Is was wondering if there’s a Ruby idiom for adding variables that should be
arrays but that may be nil, for example:

[1] + (a || [ ]) + (b || [ ]) + (c || [ ])

but that’s a bit ugly IMO. I’ve had a look, but didn’t see anything. I know I
could use a fold or something similar, but I was wondering if there was something
in the realms of ||= (i.e. nice, idiomatic shortcut) for this kind of thing?

The most idiomatic (and performant) shortcut is to not have the
situation arise in the first place. Nil checks everywhere is generally a
design smell. Push things up to whereever the defaults should be figured
out… in railsy parlance:

thingies = params[:thingies] || []

then later you never have to worry about it.

On 24 Mar 2011, at 18:53, Xavier N. wrote:

You could do something like this (untested):

[a, b, c, d].compact.reduce(:+)

though whether it is more clear is debatable. I think it reads fine if you’re
fluent.

That works, thanks, though it starts to feel a bit messy if the array
members aren’t short vars and/or have square brackets too, as it gets
pushed over several lines, and personally I dislike wrapping things with
square brackets only to take them off just to use a fold. Whereas:

a +
[b,c,d] +
e +
f

would read a lot better in that kind of situation. If it could be done
:slight_smile: The short vars in my example were due to typing laziness, I was
thinking more for stuff like this:

HEADERS = ENV["BLAH_BLAH"] + ENV["BLAH_BLAH_BLAH_BLAH"]

which begins to look unwieldy even with just two fairly long vars:

HEADERS = [ENV["BLAH_BLAH"] + 

ENV[“BLAH_BLAH_BLAH”]].compact.reduce(:+)

but

HEADERS = ENV["BLAH_BLAH"]           +
          ENV["BLAH_BLAH_BLAH"]      +
          ENV["BLAH_BLAH_BLAH_BLAH"]

will remain clear even as the vars stack up.

Much appreciated though.

Iain

On 24 Mar 2011, at 22:45, Ryan D. wrote:

The most idiomatic (and performant) shortcut is to not have the situation arise
in the first place. Nil checks everywhere is generally a design smell. Push things
up to whereever the defaults should be figured out… in railsy parlance:

thingies = params[:thingies] || []

then later you never have to worry about it.

I agree to an extent, but even when you’re defining defaults it would be
nice:

thingies = params[:thingies] || []
wotsits =  params[: wotsits] || []
stuff = thingies + wotsits

isn’t as nice as

stuff = params[:thingies] +? params[:wotsits]

where +? is my new infix operator of choice for “add unless nil” :slight_smile:
Sometimes I don’t want the extra typing or to use extra locals that will
only be used for that one line.

Iain

On Fri, Mar 25, 2011 at 7:52 AM, Robert K.
[email protected] wrote:

What about

res = [1]
[a,b,c].each {|x| x and res.concat x}

I’d also wondered whether Iain B. really needed array + array2
rather than array.concat(array2), and wondered about something
similar:

class Array
def concat_not_nil( *args )
args.each { |arg| concat(arg) unless arg.nil? }
self
end
def plus_not_nil( *args )
new_ary = self.dup
new_ary.concat_not_nil( *args )#
end
end

On Fri, Mar 25, 2011 at 7:02 AM, Iain B. [email protected]
wrote:

Much appreciated though.
What about

res = [a,b,c].inject([1]) {|r,x| x and r.concat(x) or r}

or

res = [a,b,c].inject([1]) {|r,x| x ? r.concat(x) : r}

or

res = [1]
[a,b,c].each {|x| x and res.concat x}

Kind regards

robert

On 25 Mar 2011, at 14:32, Colin B. wrote:

I really appreciate the responses, but if I apply them to the code that
provoked the question for me, and my dislike of wrapping things in an
array only to get access to a function or block (which also then removes
the wrapper), and of creating locals for a single use, then I’m not so
sure they work. As in, there are so many great shortcuts in Ruby that
feel very natural too, that some kind of infix operator works well for
args split over several lines due to their length.

I think this reads very easily, HEADERS is 3 env vars added up into one:

HEADERS = ENV["BLAH_BLAH"]           +
          ENV["BLAH_BLAH_BLAH"]      +
          ENV["BLAH_BLAH_BLAH_BLAH"]

to this, which creates an array then destroys it, which kind of masks
the intention:

HEADERS = [
            ENV["BLAH_BLAH"],
            ENV["BLAH_BLAH_BLAH"],
            ENV["BLAH_BLAH_BLAH_BLAH"]
          ].inject([1]) {|r,x| x and r.concat(x) or r}

If I was going to use a fold or something similar then I would probably
have done something like this, or used a ternary operator in a fold,
(knowing the way I think):

HEADERS = [
            ENV["BLAH_BLAH"],
            ENV["BLAH_BLAH_BLAH"],
            ENV["BLAH_BLAH_BLAH_BLAH"]
          ].reject{|x| x.nil? }.reduce(:+)

but compared to this it seems a bit creaky:

HEADERS = ENV["BLAH_BLAH"]           +?
          ENV["BLAH_BLAH_BLAH"]      +?
          ENV["BLAH_BLAH_BLAH_BLAH"]

Is there a way to define infix operators in Ruby, as I quite like this
one? :slight_smile:

Actually, looking at the code they’d be ENV[“BLAH_BLAH”].split(":") as
they were PATHs, but I’m not sure the inclusion adds anything to the
examples.

Regards,
Iain

On Fri, Mar 25, 2011 at 4:42 PM, Iain B. [email protected]
wrote:

I’d also wondered whether Iain B. really needed array + array2
new_ary.concat_not_nil( *args )#
end
end

I really appreciate the responses, but if I apply them to the code that provoked
the question for me, and my dislike of wrapping things in an array only to get
access to a function or block (which also then removes the wrapper), and of
creating locals for a single use, then I’m not so sure they work.

Maybe you should reduce your requirements…

HEADERS = [
ENV[“BLAH_BLAH_BLAH_BLAH”]
].reject{|x| x.nil? }.reduce(:+)

but compared to this it seems a bit creaky:

HEADERS = ENV[“BLAH_BLAH”] +?
ENV[“BLAH_BLAH_BLAH”] +?
ENV[“BLAH_BLAH_BLAH_BLAH”]

Is there a way to define infix operators in Ruby, as I quite like this one? :slight_smile:

There is no way to define new operators other than hacking the
interpreter. IMHO it’s a bad idea to do this just for a minor
nuisance.

Actually, looking at the code they’d be ENV[“BLAH_BLAH”].split(":") as they were
PATHs, but I’m not sure the inclusion adds anything to the examples.

I don’t understand what’s wrong with

HEADERS = []
%w{BLAH BLAH_BLAH BLAHHH}.each do |var|
x = ENV[var] and HEADERS.concat(x.split(/:+/))
end

Nice short concise and yet readable.

Cheers

robert

…personally i think it would be nice to be able to define new
operators
(like the aforementioned +?) that way when a situation like this comes
up,
one can simply:

op_def +? # maybe? i’m assuming it’d have special syntax…

stuff

end

and get on with life, making your code cleaner & simpler. IDK how well
this
would work or its potential ramifications, but i think it fits into the
‘flavor’ of ruby to be able to do such things (i mean, you can
define/change
damn near everything else in the language, why not operators?)
hex

On Sat, Mar 26, 2011 at 12:42:25AM +0900, Iain B. wrote:

Is there a way to define infix operators in Ruby, as I quite like this one? :slight_smile:

Actually, methods are kinda like infix operators anyway. One argument
is
followed by the method name, which is followed by any other argument(s):

irb(main):001:0> 4.div 2
=> 2

The div method fits between the operands, when viewed as an operator.
In
fact, that’s how other infix operators work:

irb(main):002:0> 4./ 2
=> 2

I don’t know of any way to eliminate the requirement for the dot used
with methods you define, the way built-in operators do, though.

On 25 Mar 2011, at 16:07, Robert K. wrote:

Is there a way to define infix operators in Ruby, as I quite like this one? :slight_smile:

There is no way to define new operators other than hacking the
interpreter. IMHO it’s a bad idea to do this just for a minor
nuisance.

Well, yeah, I won’t be hacking the interpreter for this :slight_smile: Just
wondering. Is there somewhere I could suggest +? as an operator for
Ruby 2? It was only an off the cuff remark but I quite like it :slight_smile:

I don’t understand what’s wrong with

HEADERS = []
%w{BLAH BLAH_BLAH BLAHHH}.each do |var|
x = ENV[var] and HEADERS.concat(x.split(/:+/))
end

Nice short concise and yet readable.

I totally agree, there’s nothing wrong with that, and out of the
suggestions given I think that’s the one I like best (which makes me
feel better for labouring the point:) It’s only that, as I said, Ruby
usually has some really nice shortcuts that don’t seem like shortcuts,
just natural looking. A bit like having to define getters and setters,
there’s nothing wrong with it, but attr_accessor :var is really nice,
as is ||= and it’s ilk.

Wouldn’t this be quite nice, for instance?

[a,b,c,d].reduce(:+?)

That’s all.

Regards,
Iain

On Mar 24, 2011, at 22:33 , Iain B. wrote:

I agree to an extent, but even when you’re defining defaults it would be nice:

thingies = params[:thingies] || []
wotsits = params[: wotsits] || []
stuff = thingies + wotsits

isn’t as nice as

stuff = params[:thingies] +? params[:wotsits]

where +? is my new infix operator of choice for “add unless nil” :slight_smile: Sometimes I
don’t want the extra typing or to use extra locals that will only be used for that
one line.

Yeah. Well… I disagree. We really don’t need extra magical syntax. I
can almost guarantee you that this proposal would get rejected by
[email protected], but you’re free to ask.

On Mar 25, 2011, at 14:22 , Iain B. wrote:

Yeah. Well… I disagree. We really don’t need extra magical syntax. I can
almost guarantee you that this proposal would get rejected by [email protected], but
you’re free to ask.

I find that a strange argument, applied across the language and a lot of what is
regularly used would be binned. Aliases, attributes and method synonyms would all
go immediately.

Well I’m not applying across the whole language… Otherwise we’d all be
coding in assembly. :smiley:

On 25 Mar 2011, at 19:06, serialhex wrote:

‘flavor’ of ruby to be able to do such things (i mean, you can define/change
damn near everything else in the language, why not operators?)
hex

I agree. In Haskell, for instance, you can make a prefix function into
an infix by surrounding it with backticks, e.g.

plus 1 2

becomes

1 `plus` 2

Something similar would be helpful on occasion, IMO.

On 25 Mar 2011, at 20:06, Ryan D. wrote:

Yeah. Well… I disagree. We really don’t need extra magical syntax. I can
almost guarantee you that this proposal would get rejected by [email protected], but
you’re free to ask.

I find that a strange argument, applied across the language and a lot of
what is regularly used would be binned. Aliases, attributes and method
synonyms would all go immediately.

Regards,
Iain

On 25.03.2011 22:22, Iain B. wrote:

becomes

 1 `plus` 2

Something similar would be helpful on occasion, IMO.

But that’s something different than defining a new operator. It’s just
fixed syntax for a method call. That approach doesn’t suffer from the
problems I laid out in my other reply to this thread.

Kind regards

robert

An alternative for the original poster’s ENV variables potentially
containing colon-separated paths:

%w{FOO BAR BAZ}.map{|x| ENV[x].to_s}.join(’:’).split(/:+/)

The .to_s should handle nil just fine. Concatenate all the strings,
then do the split.

Aaron out.

On 25.03.2011 20:06, serialhex wrote:

‘flavor’ of ruby to be able to do such things (i mean, you can define/change
damn near everything else in the language, why not operators?)

Because that would completely break parsing. Whenever an operator
definition would be seen the whole source would need reparsing - or at
least part of it. It would be tricky to determine which part. Think of
situations like this:

def foo
x +? y
end

op_def +?(a,b)
p a, b
a + b
end

Now, Ruby would have bailed out with a syntax error already before it
could read the new operator definition. If you defer error reporting to
a later point in time then execution would become much more complex
because parsing would have to occur chunk wise. And the whole thing
will likely get slower as well.

If you try something like prescanning all sources for operator
definitions before parsing the code then this is not possible because
you can easily construct ‘require’ and ‘load’ statements which do not
have a String constant argument but whose argument is constructed at
runtime. So now you have a chicken egg issue: you need to parse the
code in order to be able to execute it but you also need to execute the
code in order to be able to parse it…

And then of course there are issues of readability. In general I’d say,
too much flexibility can rather harm than do good. It’s the right
balance which is most productive. And IMHO Matz has done a wonderful
job at this.

Kind regards

robert

I’ve found that whenever I have a variable that may be nil, doing
.to_i or .to_s or .to_a or something similar (so long as the NilClass
supports the type conversion I need) can come in handy.

So for something like:

big_array = array_or_nil_var1 + array_or_nil_var2 + array_or_nil_var3

I would just do:

big_array = array_or_nil_var1.to_a + array_or_nil_var2.to_a +
array_or_nil_var3.to_a

Aaron out.

I was heard to muse:

An alternative for the original poster’s ENV variables potentially
containing colon-separated paths:

%w{FOO BAR BAZ}.map{|x| ENV[x].to_s}.join(’:’).split(/:+/)

The .to_s should handle nil just fine. Concatenate all the strings,
then do the split.

Append a .uniq to that as desired. Or do:
%w{FOO BAR BAZ}.map{|x| ENV[x].to_s.split(/:+/) || []}.flatten.uniq

Aaron out.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs