我有一个数据库,我将拉下面的对象数组。我想从中创建一个树结构。
当parent_id为nil时,则为顶级类别。如果parent_id不是nil,则它是parent_id的id值的子类别。
我提出的最佳解决方案是循环设置以获得顶级类别,然后继续循环直到我有条理。最终该表将少于500条记录,但不能保证。所以一遍又一遍地循环似乎真的很愚蠢。但是,我想不出另一种方法。下面是一个示例数据集及其组织方式。
[{id: 1, name: "top test 1", parent_id: nil},
{id: 2, name: "test 2", parent_id: 1},
{id: 3, name: "test 3", parent_id: 1},
{id: 4, name: "top test 4", parent_id: nil},
{id: 5, name: "test 5", parent_id: 3},
{id: 6, name: "test 6", parent_id: 4},
{id: 7, name: "test 7", parent_id: 4}]
top test 1
test 2
test 3
test 5
top test 2
test 6
test 7
从db返回的实际对象数组。仍然只是测试数据。
[#<ItemsCategory id: 2, name: "test 2", parent_id: 1, created_at: "2014-03-04 17:58:46", updated_at: "2014-03-04 17:58:46">,
#<ItemsCategory id: 3, name: "test 3", parent_id: 1, created_at: "2014-03-04 17:23:23", updated_at: "2014-03-04 17:23:23">,
#<ItemsCategory id: 5, name: "test 4", parent_id: 3, created_at: "2014-03-06 17:48:25", updated_at: "2014-03-06 17:48:25">,
#<ItemsCategory id: 1, name: "NEW test EDITED", parent_id: nil, created_at: "2014-03-04 17:57:21", updated_at: "2014-03-10 20:50:10">]
答案 0 :(得分:2)
你可以这样做:
<强>代码强>
def doit(data, indent = 2)
d = data.each_with_object({}) { |h,g| g[h[:id]] = h }
d.each {|_,h| h[:ancestor_ids] =
(h[:top_level_category_id] ? d[h[:parent_id]][:ancestor_ids] :[])+[h[:id]]}
.values
.sort_by { |h| h[:ancestor_ids] }
.each { |h| puts ' '*((h[:ancestor_ids].size-1)*indent) + "#{h[:name]}" }
end
<强>演示强>
data=[
{id: 1, name: "parent test 1", parent_id: nil, top_level_category_id: nil},
{id: 2, name: "test 2", parent_id: 1, top_level_category_id: 1},
{id: 3, name: "test 3", parent_id: 1, top_level_category_id: 1},
{id: 4, name: "parent test 4", parent_id: nil, top_level_category_id: nil},
{id: 5, name: "test 5", parent_id: 3, top_level_category_id: 4},
{id: 6, name: "test 6", parent_id: 4, top_level_category_id: 4},
{id: 7, name: "test 7", parent_id: 4, top_level_category_id: 4}
]
doit(data)
parent test 1
test 2
test 3
test 5
parent test 4
test 6
test 7
<强>解释强>
我们需要做的是添加另一个哈希元素(其键名为:ancestor_ids
),其值为哈希值:id
的数组以及所有哈希值它的祖先;即,我们要将以下元素添加到相应的哈希值中:
:ancestor_ids => [1]
:ancestor_ids => [1,2]
:ancestor_ids => [1,3]
:ancestor_ids => [4]
:ancestor_ids => [1,3,5]
:ancestor_ids => [4,6]
:ancestor_ids => [4,7]
一旦我们有了这些,我们就可以使用sort_by { |h| h[:ancestor_ids] }
以正确的顺序放置数组data
的元素。 (如果您不确定数组元素的排序方式,请查看Array#<=>。)此外,h[:ancestor_ids].size
用于确定显示结果时所需的缩进量。
计算结果如下*:
d = data.each_with_object({}) { |h,g| g[h[:id]] = h }
#=> {1=>{:id=>1, :name=>"parent test 1",...},
# 2=>{:id=>2, :name=>"test 2",...},
# 3=>{:id=>3, :name=>"test 3",...},
# 4=>{:id=>4, :name=>"parent test 4",...},
# 5=>{:id=>5, :name=>"test 5",...},
# 6=>{:id=>6, :name=>"test 6",...},
# 7=>{:id=>7, :name=>"test 7",...}}
我们执行此步骤,以便轻松找到与记录的父级对应的data
行。
e = d.each {|_,h| h[:ancestor_ids] =
(h[:top_level_category_id] ? d[h[:parent_id]][:ancestor_ids]:[])+[h[:id]]}
#=> {1=>{:id=>1,...,:ancestor_ids=>[1]},
# 2=>{:id=>2,...,:ancestor_ids=>[1, 2]},
# 3=>{:id=>3,...,:ancestor_ids=>[1, 3]},
# 4=>{:id=>4,...,:ancestor_ids=>[4]}
# 5=>{:id=>5,...,:ancestor_ids=>[1, 3, 5]},
# 6=>{:id=>6,...,:ancestor_ids=>[4, 6]},
# 7=>{:id=>7,...,:ancestor_ids=>[4, 7]}}
这会添加键为:ancestor_ids
的元素。我们不再需要密钥,因此我们将提取值,按:ancestor_ids
对其进行排序并显示结果:
f = e.values
#=> [{:id=>1,...,:ancestor_ids=>[1]},
# {:id=>2,...,:ancestor_ids=>[1, 2]},
# {:id=>3,...,:ancestor_ids=>[1, 3]},
# {:id=>4,...,:ancestor_ids=>[4]}
# {:id=>5,...,:ancestor_ids=>[1, 3, 5]},
# {:id=>6,...,:ancestor_ids=>[4, 6]},
# {:id=>7,...,:ancestor_ids=>[4, 7]}}
g = f.sort_by { |h| h[:ancestor_ids] }
#=> [{:id=>1,...,:ancestor_ids=>[1]},
# {:id=>2,...,:ancestor_ids=>[1, 2]},
# {:id=>3,...,:ancestor_ids=>[1, 3]},
# {:id=>5,...,:ancestor_ids=>[1, 3, 5]},
# {:id=>4,...,:ancestor_ids=>[4]}
# {:id=>6,...,:ancestor_ids=>[4, 6]},
# {:id=>7,...,:ancestor_ids=>[4, 7]}}
indent = 2
g.each { |h| puts ' '*((h[:ancestor_ids].size-1)*indent) + "#{h[:name]}" }
parent test 1
test 2
test 3
test 5
parent test 4
test 6
test 7
<强>点强>
:top_level_category_id
的哈希元素,考虑到顶级元素的:parent_id => nil
?e
的计算中,没有d
元素与键h[:parent_id]
或值h[:parent_id]
没有键,则生产代码会引发异常:ancestor_ids
。h
的{{1}}的每个元素Data
,当h[:id] > h[:parent_id]
不为零时,h[:parent_id]
为Data
。如果:id
的行最初未按sort_by
排序,则必须:id
{{1}}作为第一步。* 如果您尝试在家中运行它,它应该从命令行工作,但IRB和PRY无法处理以点开头的连续行
答案 1 :(得分:0)
需要单次通过边缘列表。所有节点必须一起装入内存;边缘列表必须构成一个实际的树(也就是说,没有检查森林,正确的DAG或周期)。
private static final Long DUMMY = null;
Node buildTree( Iterable< ? extends Edge > iedg ) {
Map< Long, Node > mnod = new HashMap< Long, Node >();
for ( Edge edg : iedg )
getNode( mnod, iedg.getParentId() ).addChild(
getNode( mnod, iedg.getId() ).withName( iedg.getName() )
);
return getNode( mnod, DUMMY ).firstChild();
}
private Node getNode( Map< Long, Node > mnod, Long lId ) {
Node nod = mnod.get( lId );
if ( null == nod )
mnod.put( lId, nod = new Node().withId( lId ) );
return nod;
}