新手问题 是否可改造成递归方法

xeruzo · 2019年02月20日 · 最后由 lyb124553153 回复于 2019年02月21日 · 994 次阅读

示例代码的参数名、使用等有些不规范,希望能暂时忽略一下。。。

employee 的上下级关系大概是个树结构,但是经过一段使用后有部分比较混乱,旧的递归方法变成死循环了

暂时改用了暴力获取的方法获取全部下属

需要改造的方法是 def get_all_subs,目前有试过其实数据最多到第四层 sub_ids_4 就结束了

自己有思考过改造成递归,大概是把每次算完的结果都当参数传递到下一个递归中,但是就是实现不出来,所以想请教一下。。。

贴上代码


 # 这个是在整数类型中自己加的方法,结果是用整数获取Employee对象,如 Employee.find(1) == 1.to_employee
def to_employee
  Employee.find(xx)
end

class Employee
 # 属性 id , max_level
  attr_accessor :id, :max_level

 # 获取的直属下级的员工,返回结果是一个一维数组
def all_sub_ids  
  [1,2,3,4,5,6.....]  
end

# 获取所有下属的员工id现在如下
# max_level > 1的才会需要找all_sub_ids 
  def get_all_subs
    result = []
    sub_ids_1 = [id].map{|n| n.to_employee.all_sub_ids}.flatten.compact - result
    return result if sub_ids_1.blank?
    result += sub_ids_1
    sub_ids_2 = sub_ids_1.map{|n| n.to_employee.all_sub_ids}.flatten.compact - result
    return result if sub_ids_2.blank?
    result += sub_ids_2
    sub_ids_3 = sub_ids_2.map{|n| n.to_employee.all_sub_ids}.flatten.compact - result
    return result if sub_ids_3.blank?
    result += sub_ids_3
    sub_ids_4 = sub_ids_3.map{|n| n.to_employee.all_sub_ids}.flatten.compact - result
    return result if sub_ids_4.blank?
    result += sub_ids_4
    sub_ids_5 = sub_ids_4.map{|n| n.to_employee.all_sub_ids}.flatten.compact - result
    return result if sub_ids_5.blank?
    result += sub_ids_5
    sub_ids_6 = sub_ids_5.map{|n| n.to_employee.all_sub_ids}.flatten.compact - result
    return result if sub_ids_6.blank?
    result += sub_ids_6
    sub_ids_7 = sub_ids_6.map{|n| n.to_employee.all_sub_ids}.flatten.compact - result
    return result if sub_ids_7.blank?
    result += sub_ids_7
    sub_ids_8 = sub_ids_7.map{|n| n.to_employee.all_sub_ids}.flatten.compact - result
    return result if sub_ids_8.blank?
    result += sub_ids_8
    sub_ids_9 = sub_ids_8.map{|n| n.to_employee.all_sub_ids}.flatten.compact - result
    return result if sub_ids_9.blank?
    result += sub_ids_9
    result.uniq.compact
  end



end

不忍细看你的代码。 没用递归,逐层循环的方式如下,没有测试,可能有错

def get_all_subs
  ids = [id]
  ids.each do |id|
    sub_ids = id.to_employee.all_sub_ids
    ids.push(*sub_ids) if sub_ids.present?
  end
  return ids
end
spike76 回复

感谢你的回复!你的方法我调整了下部分就可以啦~ 不用递归更好了,分分钟容易挂

def get_all_subs
  ids = [self.id]
  ids.each do |id|
    id.to_employee.all_sub_ids.each{|e_id| ids.push(e_id) unless ids.include?(e_id)}
  end
  return ids
end

😄 假设你有一个 children 方法,可以返回当前节点的所有子节点

def descendants
      children.each_with_object(children.to_a) {|child, arr|
        arr.concat child.descendants
      }.uniq
    end
# 假设当前借点是root 调用方法就是 root.descendants,这里返回的对象是AR数组,所以可以继续调用rails的方法

其实这里你没有必要自己做 有一个 gem acts_as_tree 帮你做这个功能

或者你觉得在内存里面做这个不是很效率,如果你用的是 PGSQL 的话,也可以直接用 WITH RECURSIVE 方法来把查询的事交给数据库去做

#
def descendants(*fields)
    subfields = fields.map { |e| "tablename.#{e}" }

    ApplicationRecord.connection.execute("
      WITH RECURSIVE descendants(#{fields.join(',')}) AS (
        SELECT #{fields.join(',')} FROM tablename WHERE id = #{id}
        UNION
        SELECT #{subfields.join(',')} FROM tablename JOIN descendants ON tablename.parent_id = descendants.id
      )
      SELECT * FROM descendants;
    ")
  end

# 传入的参数是你需要返回的字段,假设你用parent_id 去表示你的employee 的父子关系 ,这个方法返回的数组,所以不能调用rails的方法
需要 登录 后方可回复, 如果你还没有账号请 注册新账号