【问题标题】:simple hash merge by array of keys and values in ruby (with perl example)通过 ruby​​ 中的键和值数组进行简单的哈希合并(以 perl 为例)
【发布时间】:2011-10-28 10:04:15
【问题描述】:

在 Perl 中基于键和值数组执行哈希更新,我可以执行以下操作:

@hash{'key1','key2','key3'} = ('val1','val2','val3');

在 Ruby 中,我可以用更复杂的方式做类似的事情:

hash.merge!(Hash[ *[['key1','key2','key3'],['val1','val2','val3']].transpose ])

好的,但我怀疑这种程序的有效性。

现在我想在一行中做一个更复杂的赋值。

Perl 示例:

(@hash{'key1','key2','key3'}, $key4) = &some_function();

我不知道这样的事情是否可以通过一些简单的 Ruby 方式实现。有什么提示吗?

对于 Perl 受损者,@hash{'key1','key2','key3'} = ('a', 'b', 'c')hash slice,是这样的简写:

$hash{'key1'} = 'a';
$hash{'key2'} = 'b';
$hash{'key3'} = 'c';

【问题讨论】:

  • 问题是hash{'key1','key2','key3'} 是错误的语法。所以你不能这样做
  • Perl 的最后一行是什么?调用函数并使用函数的前三个返回值更新哈希并将局部变量设置为第四个(以及更多?)值?
  • @Phrogz:这是一个哈希片,见第二个代码块in here
  • @fl00r @hash{'key1','key2','key3'} = (1, 2, 3); 在 Perl 中是完全正确的。
  • 最后一行是 Perl,而不是 Ruby。我认为 &some_function 返回值数组,例如:[1,2,3,4]。前三个被保存到散列,所以它看起来像这样:@hash = { 'key1' => 1, 'key2' => 2, 'key3 => 3 }。第四个值存储在 $key4 中。如果 &some_function 返回超过 4 个值,$key4 将存储数组 = [4,5,...]。 (抱歉混用了 Ruby 和 Perl 语法 :)。)

标签: ruby arrays hash variable-assignment


【解决方案1】:

您可以重新定义 []= 以支持此功能:

class Hash
  def []=(*args)
    *keys, vals = args # if this doesn't work in your version of ruby, use "keys, vals = args[0...-1], args.last"
    merge! Hash[keys.zip(vals.respond_to?(:each) ? vals : [vals])]
  end
end

现在使用

myhash[:key1, :key2, :key3] = :val1, :val2, :val3
# or
myhash[:key1, :key2, :key3] = some_method_returning_three_values
# or even
*myhash[:key1, :key2, :key3], local_var = some_method_returning_four_values

【讨论】:

  • 或者,如果 OP 想要将返回值之一捕获为局部变量,您可以改为编写 *h[:a, :b, :c], d = many_values。但是,像这样对所有 Hash 进行猴子修补是完全不负责任的,并且很可能会破坏其他人的代码。这应该作为一个用于扩展任何 Hash 实例的模块来完成。
  • @Phrogz:实际上就是这样。但是我需要重新定义 Hash 类的 []= 方法,这也不符合我的喜好。
  • 我认为您还可以定义一个新方法,例如称为Hash#multi=,然后使用*h.multi(:a, :b), c = many_values。但我无法让它工作。 @Phrogz,你知道这样做的方法吗?
  • 另外,@Phrogz:这保留了原始的h[k]=v 功能,那么它会如何破坏其他代码?
  • @jtbandes h[ [1,2] ] = 3 #=> NoMethodError: undefined method 'each' for 3:Fixnum。我一直使用带有复合键的散列。
【解决方案2】:

在 Ruby 1.9 中,Hash.[] 可以将二值数组的数组作为其参数(除了替代键/值参数的平面列表的旧行为)。所以做起来比较简单:

mash.merge!( Hash[ keys.zip(values) ] )

我不知道 perl,所以我不确定你最后的“更复杂的任务”想要做什么。您能用文字或示例输入和输出来解释您想要实现的目标吗?

编辑:根据@fl00r 的回答中的讨论,您可以这样做:

def f(n)
  # return n arguments
  (1..n).to_a
end

h = {}
keys = [:a,:b,:c]
*vals, last = f(4)
h.merge!( Hash[ keys.zip(vals) ] )
p vals, last, h
#=> [1, 2, 3]
#=> 4
#=> {:a=>1, :b=>2, :c=>3}

代码*a, b = some_array 会将最后一个元素分配给b,并创建a 作为其他值的数组。此语法需要 Ruby 1.9。如果您需要 1.8 兼容性,您可以这样做:

vals = f(4)
last = vals.pop
h.merge!( Hash[ *keys.zip(vals).flatten ] )

【讨论】:

  • 我将尝试解释第二个 Perl 示例:我希望 &some_function() 返回四个值的数组。复杂的 Perl 赋值示例将分配返回数组的第一个值代替hash 的值key1,返回数组的第二个值代替key2hash 值,...最后返回数组的第四个值到key4变量中。
  • 这里真正的问题是 Perl 会将多个哈希键解释为独立的 lvalue 目标,而 Ruby 会将它们统称为一个数组。在大多数情况下,Perl 表达lvalues 的方式比 Ruby 更强大而微妙。
  • 感谢您的澄清。 Ruby 中没有 simple 的单行解决方案可以完成 Perl 代码的工作。最接近的是@fl00r 发布的内容。在 11 年的 Ruby 编码中,我认为我不需要从方法中提取返回值的子集并使用它们更新哈希值,同时保留其他返回值。我很感兴趣;你有一个有用(或必要)的用例吗?
  • @geronime 查看我的更新;这不是一条线,但它是实现目标的一种方式。
  • @Phrogz:我用 Perl 写了几年代码,现在用 Ruby 写了将近一年。我多次使用哈希切片分配。这种复杂分配的一个例子可能是这个(虚拟混合 Ruby/Perl 代码):@counters{'done','failed'}, queue_length = @redis.exec,其中@redis.exec 在一个原子事务中返回排队命令@redis.mget 'done', 'failed'@redis.llen 'queue' 的值。在 ruby​​ 中,我必须将计数器存储在一个数组中并稍后将其映射到哈希中。
【解决方案3】:

你可以这样做

def some_method
  # some code that return this:
  [{:key1 => 1, :key2 => 2, :key3 => 3}, 145]
end

hash, key = some_method
puts hash
#=> {:key1 => 1, :key2 => 2, :key3 => 3}
puts key
#=> 145

UPD

在 Ruby 中,您可以进行“并行赋值”,但不能像在 Perl 中那样使用哈希 (hash{:a, :b, :c))。但是你可以试试这个:

hash[:key1], hash[:key2], hash[:key3], key4 = some_method

some_method 返回一个包含 4 个元素的数组。

【讨论】:

  • 这不是解决方案。如果我想在右侧使用一些第三方方法的结果怎么办?写一些包装?可能不会。
  • @geronime 查看我的更新。右边的方法应该返回一个与左边相同数量的数组
  • @floor:是的,可以这样做,但如果我想通过包含三个以上元素的数组传递要更新的键,则它是不可用的。就像 Perl 中的 @keys = ('key1','key2',... lots more); @hash{@keys} = &some_function()
  • 如果您想将额外的返回值捕获为一个数组,请将key4 更改为*key4。否则其他值将被忽略,key4 将始终是第四个返回值(或更准确地说,从方法返回的单个数组中的第四个元素)。
  • @Phrogz:我知道,Perl 的行为类似(所有冗余值都将被忽略 - 要将它们存储在数组中,我只需将 $key4 标量更改为 @key4 数组)。跨度>
猜你喜欢
  • 2015-06-13
  • 2016-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-19
相关资源
最近更新 更多