解决map reduce问题

时间:2011-04-10 15:01:57

标签: ruby mapreduce

我想在ruby中模拟像hadoop这样的系统的mapreduce函数的实现,以验证该想法是否至少有效。

我有以下问题。我有两个元素列表:

List1
3 - A
4 - B
5 - C
7 - D
8 - F

List2
2 - A
8 - B
6 - C
9 - D
4 - E

我需要构建一个通用列表,其中包含与两个列表中常见字母相关联的数字总和:

commonList
5 - A
12 - B
11 - C
16 - D

我想使用mapreduce操作创建一个ruby脚本来解决此问题。我不确定如何解决这个问题,或者在ruby脚本中用什么程序来模拟这个问题。

任何帮助表示感谢。

4 个答案:

答案 0 :(得分:2)

您可以尝试考虑MapReduce维基百科文章中提供的元素:

  • 输入阅读器 - 在您的情况下,这可能是您输入哈希值[key, value]对上的方法调用。
  • 一个地图功能 - 您已经拥有了应该处理数据的密钥,因此您的map工作人员只会返回它作为输入获得的[key, value]
  • 分区函数 - 一种基于密钥分配reduce worker的方法。在您的情况下,它可能只是key.hash % REDUCER_COUNT
  • 比较功能 - 我不认为这适用于您的情况,因为您不需要按任何特定顺序处理值。
  • 一个Reduce函数 - 将被赋予[key, list]对,list是与该键相关联的值列表。如果列表长度超过一个元素,它将返回list的总和(因为您只需要处理两个输入哈希中出现的元素)。
  • 输出编写器 - 在您的示例中可以是简单的哈希。

here's我的(过)简化了上述的实现。

答案 1 :(得分:2)

假设我们已经实现了所有其他与map-reduce相关的函数(输入阅读器,输出编写器,全局排序......),这些函数将是mapreduce:< / p>

def map(input)
  input.each do |count, letter|
    yield [letter, count]
  end
end

def reduce(letter, partial_counts)
  result = if partial_counts.size == 2
    partial_counts[0] + partial_counts[1]
  end

  yield result
end

map函数将yield一对(letter, count),稍后将对其进行分组。然后,对于从letter map收到的每个reduce,我们会收到一个数组,其中包含mapletter生成的每个计数。如果您希望仅在两个哈希值上都出现字母时,我们需要count s出现在partial_counts两次以使用它来计算结尾处的总和。 reduce函数可以通过多种方式实现。我试图让它尽可能简单地理解,虽然它的实现非常适应这个问题。

使用这些mapreduce实现将返回带有键和值的最后一个哈希,这更有意义,因为可能有多个具有相同计数的字母。如果它反转键和值也会更好。这样,map就像生成每对(letter, count)

一样简单
def map(input)
  input.each do |letter, count|
    yield [letter, count]
  end
end

def map(input)
  input.each do |i|
    yield i
  end
end

答案 2 :(得分:2)

list_1 = ["3 - A", "4 - B", "5 - C", "7 - D", "8 - F"]

list_2 = ["2 - A", "8 - B", "6 - C", "9 - D", "4 - E"]

(list_1 + list_2).map do |str|
  # change array of strings to array in the form of [[name, value], ...]
  str  =~ /(\d+) - (.*)/ && [$2, $1.to_i]
end.reduce({}) do |memo, obj|
  # use a temporary Hash to sum up the values;
  # the value is an array in the form of [value_counter, iteration_counter]
  prev = memo[obj.first] || [0, 0]
  memo[obj.first] = [prev.first + obj.last, prev.last + 1]
  memo
end.map do |key, value|
  # convert to array in original format or
  # nil, if occurred only once
  value.last > 1 ? "#{key} - #{value.first}" : nil
end.compact

=> ["A - 5", "B - 12", "C - 11", "D - 16"]

此代码使用Ruby的mapreduce方法,但直接在Hash上完成所有这些操作会更加优雅。

答案 3 :(得分:2)

使用irb(ruby-1.9.2-p180):

list = [ {a:2, b:1, d:3}, {a:3, b:2, c:3}, {a:4, b:1, c:3} ]
 => [{:a=>2, :b=>1, :d=>3}, {:a=>3, :b=>2, :c=>3}, {:a=>4, :b=>1, :c=>3}] 

Hash[list.map(&:keys).inject(&:&).map{|key| [key,list.map{|arr| arr[key]}.inject(&:+)]}]
 => {:a=>9, :b=>4} 

此解决方案适用于多个数组(2+),它可以找到常用键并将它们相加返回结果哈希值

找到公共密钥(收集密钥并查找公共部分):

list.map(&:keys).inject(&:&)

查找键的总和(按键选择值并求和):

list.map{|arr| arr[key]}.inject(&:+)]

从数组[[:a,9], [:b,4]]

构建哈希
results = [[:a,9], [:b,4]]
Hash[ results ]

我喜欢这款衬里的红宝石!