Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,14 @@
"prerequisites": [],
"difficulty": 5
},
{
"slug": "tree-building",
"name": "Tree Building",
"uuid": "088fcf19-851f-4fb9-a740-7d9d14362432",
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "rectangles",
"name": "Rectangles",
Expand Down
24 changes: 24 additions & 0 deletions exercises/practice/tree-building/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Instructions

Refactor a tree building algorithm.

Some web-forums have a tree layout, so posts are presented as a tree.
However the posts are typically stored in a database as an unsorted set of records.
Thus when presenting the posts to the user the tree structure has to be reconstructed.

Your job will be to refactor a working but slow and ugly piece of code that implements the tree building logic for highly abstracted records.
The records only contain an ID number and a parent ID number.
The ID number is always between 0 (inclusive) and the length of the record list (exclusive).
All records have a parent ID lower than their own ID, except for the root record, which has a parent ID that's equal to its own ID.

An example tree:

```text
root (ID: 0, parent ID: 0)
|-- child1 (ID: 1, parent ID: 0)
| |-- grandchild1 (ID: 2, parent ID: 1)
| +-- grandchild2 (ID: 4, parent ID: 1)
+-- child2 (ID: 3, parent ID: 0)
| +-- grandchild3 (ID: 6, parent ID: 3)
+-- child3 (ID: 5, parent ID: 0)
```
67 changes: 67 additions & 0 deletions exercises/practice/tree-building/.meta/TreeBuilding.example.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
Class Record {
$RecordId
$ParentId

Record($RecordId, $parentId) {
$this.RecordId = $RecordId
$this.ParentId = $parentId
}

[void] Validate($upperBound){
if ($this.RecordId -ge $upperBound) {
Throw "Record id is invalid or out of order."
}
if ($this.RecordId -eq $this.ParentId -and $this.RecordId -ne 0) {
Throw "Only root should have equal record and parent id (0)."
}
if ($this.RecordId -lt $this.ParentId) {
Throw "Node record ID should be greater than parent id."
}
}
}

Class Node {
[int] $NodeID
[int] $ParentID
[Node[]] $Children

Node([Record] $record) {
$this.NodeID = $record.RecordId
$this.ParentID = $record.ParentId
$this.Children = @()
}

[bool] IsLeaf() {
return $this.Children.Count -eq 0
}
}

Function Build-Tree() {
<#
.DESCRIPTION
Building tree function

.PARAMETER Records
Records used to build tree
#>
[CmdletBinding()]
Param(
[Record[]] $Records
)
$upperBound = $Records.Count

$Records
| Sort-Object RecordId
| ForEach-Object {$nodes = @{}} {
$_.Validate($upperBound)
$nodes[$_.RecordId] = [Node]::new($_)
}

foreach ($id in $nodes.Keys) {
if ($id -ne 0) {
$parent = $nodes[$id].ParentId
$nodes[$parent].Children = @($nodes[$id]) + $nodes[$parent].Children
}
}
$nodes[0]
}
20 changes: 20 additions & 0 deletions exercises/practice/tree-building/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"authors": [
"glaxxie"
],
"contributors": [
"clapmyhands"
],
"files": {
"solution": [
"TreeBuilding.ps1"
],
"test": [
"TreeBuilding.tests.ps1"
],
"example": [
".meta/TreeBuilding.example.ps1"
]
},
"blurb": "Refactor a tree building algorithm."
}
104 changes: 104 additions & 0 deletions exercises/practice/tree-building/TreeBuilding.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<#
.SYNOPSIS
Refactor a tree building algorithm.

.DESCRIPTION
The code below are ugly, confusing and slow.
Not to mention it also failed all the test that need to raise error.
Feel free to rework it however you want to pass the test suite.
#>

# this stub is adapted from the python track
Class Record {
$RecordId
$ParentId

Record($RecordId, $parentId) {
$this.RecordId = $RecordId
$this.ParentId = $parentId
}
}

Class Node {
[int] $NodeID
[Node[]] $Children

Node($id) {
$this.NodeID = $id
$this.Children = @()
}

[bool] IsLeaf() {
if ($this.HasChildren()) {
return $false
}else {
return $true
}
}

[bool] HasChildren() {
if ($this.Children.Count -gt 0) {
return $true
}else {
return $false
}
}
}

Function Build-Tree() {
<#
.DESCRIPTION
Building tree function

.PARAMETER Records
Records used to build tree
#>
[CmdletBinding()]
Param(
[Record[]] $Records
)
$root = $null

$sortedRecords = $Records | Sort-Object RecordId

$ordered_id = foreach ($r in $sortedRecords) {
$r.RecordId
}

$trees = @()
$parent = @{}

foreach ($i in 0..($ordered_id.Count - 1)) {
foreach ($j in $sortedRecords) {
if ($ordered_id[$i] -eq $j.RecordId) {
$trees += [Node]::new($ordered_id[$i])
}
}
}

foreach ($i in 0..($ordered_id.Length - 1)) {
foreach ($j in $trees) {
if ($i -eq $j.NodeID) {
$parent = $j
}
}
foreach ($j in $sortedRecords) {
if ($j.ParentId -eq $i) {
foreach ($k in $trees) {
if ($k.NodeID -eq 0) {
continue
}
if ($j.RecordId -eq $k.NodeID) {
$child = $k
$parent.Children += $child
}
}
}
}
}

if ($trees.Count -gt 0) {
$root = $trees[0]
}
return $root
}
Loading