Ruby根据子集数组的内容重新排序数组

时间:2014-05-29 19:25:22

标签: ruby arrays

我按hash[:points]

排序此数组
original = [{a: "Bill", points: 4}, {b: "Will", points: 3}, {c: "Gill", points: 2}, {d: "Pill", points: 1}]

我想根据子集数组的顺序更改其元素的顺序,也按hash[:points]排序。

subset = [{c: "Gill", points: 2}, {b: "Will", points: 1}]

子集的元素始终包含在原始元素中。但是子集的长度和顺序是随机的。它可以有任何顺序的两个,三个或四个元素。

我想将子集的顺序合并到原始数组中。这可以通过重新排序原件,或以正确的顺序重新创建原件来完成。要么会这样做。但我不想合并它们。子集中的keysvalues并不重要,只是元素的顺序。

例如,上面的子集应该产生这个。

# Bill and Pill retain their original position
# Gill and Will swap places as per ordering of the subset
[{a: "Bill", points: 4}, {c: "Gill", points: 2}, {b: "Will", points: 3}, {d: "{Pill}", points: 1}]

此子集的另一个示例:[{c: "Gill", points: 3}, {b: "Will", points: 2}, {a: "Bill", points: 1}]

# Pill remains at index 3 since it was not in the subset
# Gill, Will, and Bill are reordered based on the order of the subset 
[{c: "Gill", points: 3}, {b: "Will", points: 2}, {a: "Bill", points: 1}, {d: "Pill", points: 1}] 

在过去的几个小时里,我尝试了很多东西,但我发现它比看起来更难。

3 个答案:

答案 0 :(得分:2)

我的解决方案有两个步骤:

  1. 从原始数组中收集相关元素,并根据子集顺序对它们进行排序。
  2. 使用新订单在原始数组中替换它们。
  3. 以下是代码:

    mapped_elements = subset.map { |i| original.find { |j| j.keys == i.keys } }
    
    result = original.map do |i|
      if subset.find { |j| j.keys == i.keys }
        mapped_elements.shift
      else
        i
      end
    end
    

    对于subset = [{c: "Gill", points: 2}, {b: "Will", points: 1}],结果将为:

    [{a: "Bill", points: 4}, {c: "Gill", points: 2}, {b: "Will", points: 3}, {d: "{Pill}", points: 1}]
    

    对于subset = [{c: "Gill", points: 3}, {b: "Will", points: 2}, {a: "Bill", points: 1}],结果将为:

    [{c: "Gill", points: 3}, {b: "Will", points: 2}, {a: "Bill", points: 4}, {d: "Pill", points: 1}] 
    

答案 1 :(得分:1)

I know this sounds weird, but trust me, there is a very good reason for it.

对不起,没有办法。我认为你开始选择错误的哈希结构。我想不出为什么你会为每个人的名字创建具有不同键的哈希的任何理由。当您在操作最初选择的数据结构时遇到问题,那么您应该考虑重构数据。

teams = { 
  Bill: {group: "a", points: 4},
  Will: {group: "b", points: 3},
  Gill: {group: "c", points: 2},
  Pill: {group: "d", points: 1},
}

teams_subset = {
  Gill: {group: "c", points: 3}, 
  Will: {group: "b", points: 2}, 
  Bill: {group: "a", points: 1},
}

subset_names =  teams_subset.keys
new_teams = {}

teams.each do |team, stats|
  if teams_subset.include? team
    next_subset_name = subset_names.shift
    new_teams[next_subset_name] = teams[next_subset_name]
  else 
    new_teams[team] = stats
  end
end 

p new_teams

--output:--
{:Gill=>{:group=>"c", :points=>2}, :Will=>{:group=>"b", :points=>3}, :Bill=>{:group=>"a", :points=>4}, :Pill=>{:group=>"d", :points=>1}}

甚至:

teams =  [
  {name: 'Bill', stats: {group: "a", points: 4}},
  {name: 'Will', stats: {group: "b", points: 3}},
  {name: 'Gill', stats: {group: "c", points: 2}},
  {name: 'Pill', stats: {group: "d", points: 1}},
]

根据您的新发现,我只想使用准Schwarzian变换将您的数据转换为此形式:

"Bill"=>{:name=>"Bill", :points=>4}

...然后应用类似于我上面发布的代码,如下所示:

require 'set'

teams =  [
  {name: 'Bill', points: 4},
  {name: 'Will', points: 3},
  {name: 'Gill', points: 2},
  {name: 'Pill', points: 1},
]

remapped_teams = {}

teams.each do |hash|
  name = hash[:name]
  remapped_teams[name] = hash
end

p remapped_teams

#--output:--
#{"Bill"=>{:name=>"Bill", :points=>4}, "Will"=>{:name=>"Will", :points=>3}, "Gill"=>{:name=>"Gill", :points=>2}, "Pill"=>{:name=>"Pill", :points=>1}}

teams_subset = [
  {name: 'Gill', points: 3}, 
  {name: 'Will', points: 2}, 
  {name: 'Bill', points: 1},
]

subset_names = teams_subset.map do |hash|
  hash[:name]
end
subset_names_lookup = Set.new(subset_names)

new_team_order = remapped_teams.map do |(name, hash)|
  if subset_names_lookup.include? name
    remapped_teams[subset_names.shift]
  else 
    hash
  end
end 

p new_team_order

--output:--
[{:name=>"Gill", :points=>2}, {:name=>"Will", :points=>3}, {:name=>"Bill", :points=>4}, {:name=>"Pill", :points=>1}]

答案 2 :(得分:1)

这是另一种方法。这是基于以下理解:originalsubset中的每个哈希包含相同的两个密钥::name(比方说)和:points

<强>代码

def reorder(original, subset)
  orig_hash = original.each_with_object({}) { |h,g| g[h[:name]] = h }
  subset_names = subset.map { |h| h[:name] }
  orig_hash.map { |k,v|
    subset_names.include?(k) ? orig_hash[subset_names.rotate!.last] : v }
end

<强>实施例

original = [{name: "Bill", points: 4}, {name: "Will", points: 3},
            {name: "Gill", points: 2}, {name: "Pill", points: 1}]

#1

subset   = [{name: "Gill", points: 2}, {name: "Will", points: 1}]
reorder(original, subset)
  #=> [{:name=>"Bill", :points=>4}, {:name=>"Gill", :points=>2},
  #    {:name=>"Will", :points=>3}, {:name=>"Pill", :points=>

#2

subset   = [{name: "Gill", points: 3}, {name: "Will", points: 2},
            {name: "Bill", points: 1}]

reorder(original, subset)
  #=> [{:name=>"Gill", :points=>2}, {:name=>"Will", :points=>3},
  #    {:name=>"Bill", :points=>4}, {:name=>"Pill", :points=>1}]

<强>解释

以上original

执行以下计算
subset = [{c: "Gill", points: 2}, {b: "Will", points: 1}]

original构建此哈希:

orig_hash = original.each_with_object({}) { |h,g| g[h[:name]] = h }
  #=> {"Bill"=>{:name=>"Bill", :points=>4}, "Will"=>{:name=>"Will", :points=>3},
      "Gill"=>{:name=>"Gill", :points=>2}, "Pill"=>{:name=>"Pill", :points=>1}}

以及来自:name的{​​{1}}值的数组:

subsets

剩下的就是将subset_names = subset.map { |h| h[:name] } #=> ["Gill", "Will"] 的每个k=>v元素映射到orig_hash {如果v没有密钥subset_names)或者k subset_names的第一个元素。由于我们无法从subset_names中删除后者,因此我选择将数组旋转+1,然后从最后一个位置检索该值。这样,subset_names中的下一个键将在阵列的开头正确定位。

orig_hash.map { |k,v| subset_names.include?(k) ? orig_hash[subset_names.rotate!.last] : v }
  #=> [{:name=>"Bill", :points=>4}, {:name=>"Gill", :points=>2},
  #    {:name=>"Will", :points=>3}, {:name=>"Pill", :points=>1}]