Best way to make sub-hash from hash with specific keys

I have a a function which looks like this:

    def rename(basename: true, extension: '.txt') ...

I have a configuration hash, e.g.

    config = {basename: false, extension: '.pdf', offset: 10, something:
'else'}

I want to call the function:

    rename(**config)

but clearly this doesn't work because some arguments are specified, e.g.
:offset which are not included in the function. One way to deal with this
is:

        def rename(basename: true, extension: '.txt', **options) ...

but I'm not sure I'm happy with this since it might mean that if the
definition of rename changes, some configuration options that shouldn't be
provided suddenly become meaningful (and/or do the wrong thing).

So, I'd like to make a sub-hash, e.g.

    Hash[[:basename, :extension].collect{|key| [key, config[key]]}]

but I'm not sure if this is the best approach either. It seems sort of
clumsy. It would be nice if there was a method like config.select(keys) or
something similar, but I couldn't find this.

Does anyone have any suggestions or ideas about what to do here? Thanks.

What's interesting, is that just adding **options to the function incurs a
30% performance hit too, in the case where there are actually additional
options:

require 'benchmark/ips'

class OptionsAny
def sum(a: nil, b: nil, c: nil, **options)
a+b+c
end
end

class OptionsStrict
def sum(a: nil, b: nil, c: nil)
a+b+c
end
end

Benchmark.ips do |x|
x.report("any") do |i|
options = OptionsAny.new
config = {a: 10, b: 20, c: 30}
i.times {options.sum(**config)}
end
x.report("strict") do |i|
options = OptionsStrict.new
config = {a: 10, b: 20, c: 30}
i.times {options.sum(**config)}
end
x.compare!
end

irb(main):001:0> config = {basename: false, extension: '.pdf', offset:
10, something: 'else'}
=> {:basename=>false, :extension=>".pdf", :offset=>10, :something=>"else"}
irb(main):002:0> config.entries
=> [[:basename, false], [:extension, ".pdf"], [:offset, 10],
[:something, "else"]]
irb(main):003:0> keys=[:basename,:extension];config.entries.select
{|k,v| keys.include? k}
=> [[:basename, false], [:extension, ".pdf"]]
irb(main):004:0>
keys=[:basename,:extension];Hash[config.entries.select {|k,v|
keys.include? k}]
=> {:basename=>false, :extension=>".pdf"}

Kind regards

robert

···

On Sun, Jun 5, 2016 at 7:26 AM, Samuel Williams <space.ship.traveller@gmail.com> wrote:

So, I'd like to make a sub-hash, e.g.

    Hash[[:basename, :extension].collect{|key| [key, config[key]]}]

but I'm not sure if this is the best approach either. It seems sort of
clumsy. It would be nice if there was a method like config.select(keys) or
something similar, but I couldn't find this.

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

why not get the needed parts of the hash at the start of your routine,
then clear the hash thereafter.
like,

def rename config
   clean_config=config.slice(:basename, :extension) and config.clear
   ...

kind regards,
--botp

···

On Sun, Jun 5, 2016 at 1:26 PM, Samuel Williams <space.ship.traveller@gmail.com> wrote:

        def rename(basename: true, extension: '.txt', **options) ...

but I'm not sure I'm happy with this since it might mean that if the
definition of rename changes, some configuration options that shouldn't be
provided suddenly become meaningful (and/or do the wrong thing).

Which version of Ruby? In (mainline) Ruby 2.2+, keyword args
handling was optimized for pure Ruby methods to avoid allocating
a temporary hash.

Unfortunately methods implemented in C still suffer from the
hash allocation.

···

Samuel Williams <space.ship.traveller@gmail.com> wrote:

What's interesting, is that just adding **options to the function incurs a
30% performance hit too, in the case where there are actually additional
options:

Hmm, I'm using 2.3.1 for testing.

I found two solutions - facets provides Hash#slice (also ActiveSupport has
a slightly more noisy implementation).

In the end, I filed a feature request:
https://bugs.ruby-lang.org/issues/12461

I sort of feel like this wouldn't be a bad feature to have built directly
into Ruby's Hash implementation since now it's very useful in conjunction
with named parameters.

···

On 5 June 2016 at 22:02, Eric Wong <e@80x24.org> wrote:

Samuel Williams <space.ship.traveller@gmail.com> wrote:
> What's interesting, is that just adding **options to the function incurs
a
> 30% performance hit too, in the case where there are actually additional
> options:

Which version of Ruby? In (mainline) Ruby 2.2+, keyword args
handling was optimized for pure Ruby methods to avoid allocating
a temporary hash.

Unfortunately methods implemented in C still suffer from the
hash allocation.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

def rename config
   clean_config=config.slice(:basename, :extension) and config.clear
   ...

wc can be shortened still to,

def rename config
    config.slice!(:basename, :extension)
    ...

···

On Mon, Jun 6, 2016 at 7:12 PM, botp <botpena@gmail.com> wrote:

kind regards,
--botp

Interesting, kwargs shouldn't hurt performance, there.
Can you show me the benchmark result?

Maybe it's platform-dependent, too (perhaps malloc-related).
I tried it yesterday on trunk (x86-64, old Debian 7.x, glibc
malloc) and saw it was within measurement error or maybe I'm
reading the results, wrong.

I don't have any opinion on the feature itself, though.

···

Samuel Williams <space.ship.traveller@gmail.com> wrote:

Hmm, I'm using 2.3.1 for testing.

Eric, something like this:

require 'benchmark/ips'

class OptionsAny
def sum(a: nil, b: nil, c: nil, **options)
a+b+c
end
end

class OptionsStrict
def sum(a: nil, b: nil, c: nil, d: nil)
a+b+c
end
end

Benchmark.ips do |x|
x.report("any") do |i|
options = OptionsAny.new
config = {a: 10, b: 20, c: 30, d: 40}
i.times {options.sum(**config)}
end
x.report("strict") do |i|
options = OptionsStrict.new
config = {a: 10, b: 20, c: 30, d: 40}
i.times {options.sum(**config)}
end
x.compare!
end

My results in 2.3.0:

Comparison:

              strict: 513553.0 i/s

                 any: 408048.9 i/s - 1.26x slower

···

On 7 June 2016 at 01:26, Eric Wong <e@80x24.org> wrote:

Samuel Williams <space.ship.traveller@gmail.com> wrote:
> Hmm, I'm using 2.3.1 for testing.

Interesting, kwargs shouldn't hurt performance, there.
Can you show me the benchmark result?

Maybe it's platform-dependent, too (perhaps malloc-related).
I tried it yesterday on trunk (x86-64, old Debian 7.x, glibc
malloc) and saw it was within measurement error or maybe I'm
reading the results, wrong.

I don't have any opinion on the feature itself, though.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>

Even if you write:

class OptionsAny
def sum(a: nil, b: nil, c: nil, **)
a+b+c
end
end

where you are not interested in the options hash, it's still about the same
level of performance (~30% slower).

···

On 7 June 2016 at 01:26, Eric Wong <e@80x24.org> wrote:

Samuel Williams <space.ship.traveller@gmail.com> wrote:
> Hmm, I'm using 2.3.1 for testing.

Interesting, kwargs shouldn't hurt performance, there.
Can you show me the benchmark result?

Maybe it's platform-dependent, too (perhaps malloc-related).
I tried it yesterday on trunk (x86-64, old Debian 7.x, glibc
malloc) and saw it was within measurement error or maybe I'm
reading the results, wrong.

I don't have any opinion on the feature itself, though.

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk>