🍁
Swift: 大抵のObjectをTree表示する
Swiftの場合、Dictionary
をJSON形式でPrettyPrintするのはJSONSerialization
を使えば簡単です。→オブジェクトをPrettyPrint
ただ、Tree形式で表示するとなると自前実装が必要そうだったので、書いてみました。
import Foundation
struct SwiftTree {
enum RuledLine {
static let root = "."
static let stem = "│ "
static let branch = "├──"
static let lastBranch = "└──"
static let space = " "
}
enum Parent {
case none
case array
case dictionary
}
static func print(_ obj: Any?) {
let output = SwiftTree.makeTree(obj: obj).joined(separator: "\n")
Swift.print(output)
}
private static func makeTree(obj: Any?, parent: Parent = .none, isLast: Bool = false, prefix: [String] = []) -> [String] {
var result = [String]()
if let dict = obj as? [String: Any?] { // Dictionay
let array: [(key: String, value: Any?)] = dict.sorted { $0.key < $1.key }
if parent == .none {
result.append(RuledLine.root)
} else if parent == .array {
result.append(contentsOf: makeTree(obj: RuledLine.root,
parent: .array,
isLast: isLast,
prefix: prefix))
}
for i in (0 ..< array.count) {
let isLastValue: Bool = (i == array.count - 1)
var keyPrefix = prefix
if parent == .array {
keyPrefix.append(isLast ? RuledLine.space : RuledLine.stem)
}
result.append(contentsOf: makeTree(obj: array[i].key,
parent: .dictionary,
isLast: isLastValue,
prefix: keyPrefix))
let valuePrefix = keyPrefix + [isLastValue ? RuledLine.space : RuledLine.stem]
result.append(contentsOf: makeTree(obj: array[i].value,
parent: .dictionary,
isLast: true,
prefix: valuePrefix))
}
} else if let array = obj as? [Any?] { // Array
if array.isEmpty {
let isLastValue: Bool = (parent == .dictionary ? true : isLast)
result.append(contentsOf: makeTree(obj: "empty",
parent: .array,
isLast: isLastValue,
prefix: prefix))
} else {
if parent == .none {
result.append(RuledLine.root)
} else if parent == .array {
result.append(contentsOf: makeTree(obj: RuledLine.root,
parent: .array,
isLast: isLast,
prefix: prefix))
}
for i in (0 ..< array.count) {
let isLastValue: Bool = i == (array.count - 1)
var valuePrefix = prefix
if parent == .array {
valuePrefix.append(isLast ? RuledLine.space : RuledLine.stem)
}
result.append(contentsOf: makeTree(obj: array[i],
parent: .array,
isLast: isLastValue,
prefix: valuePrefix))
}
}
} else { // Element
let valuePrefix = prefix + [isLast ? RuledLine.lastBranch : RuledLine.branch]
let line = (valuePrefix + ["\(obj ?? "nil")"]).joined(separator: " ")
result.append(line)
}
return result
}
}
このSwiftTree.print(_:)
を使えば、たとえば以下のような複雑なオブジェクトでもTree形式で表示できます。(もちろんnil
でも大丈夫)
複雑なオブジェクトの例
let dict: [String: Any?] = [
"Drink": [
"RedBull",
"Monster",
"RealGold",
],
"Price": [
108,
216,
300.33
],
"Food": [
"Sushi": [
"Maguro",
"Sake",
"Ikura",
"Uni",
],
"Yakiniku": [
"Harami",
"Karubi",
nil
],
],
"Piyo": [
[
"Meu": 1,
"Foo": 2,
"Gao": 3
],
[
"Jake": 4,
"Gomi": 5
]
],
"Hoge": nil
]
出力結果の例
.
├── Drink
│ ├── RedBull
│ ├── Monster
│ └── RealGold
├── Food
│ ├── Sushi
│ │ ├── Maguro
│ │ ├── Sake
│ │ ├── Ikura
│ │ └── Uni
│ └── Yakiniku
│ ├── Harami
│ ├── Karubi
│ └── nil
├── Hoge
│ └── nil
├── Piyo
│ ├── .
│ │ ├── Foo
│ │ │ └── 2
│ │ ├── Gao
│ │ │ └── 3
│ │ └── Meu
│ │ └── 1
│ └── .
│ ├── Gomi
│ │ └── 5
│ └── Jake
│ └── 4
└── Price
├── 108.0
├── 216.0
└── 300.33
Discussion
配列の要素が一つしかないときや辞書のキーが一つしかないときに、Treeをさらにコンパクトにまとめたい場合。