Preface:

You can do it in three steps,

  • Text parsing, extracting information

  • Sort out the hierarchy of dependency diagrams

  • Draw out

This is an introduction to functional programming to parse and extract information

Functional Programming and Text parsing, Review Octree/Pretty

This article introduces the process oriented, using regular expressions, parsing text,

The second step is to sort out the dependency map

Any reference octree/pretty

The first step, text parsing, extract information

Process oriented, friendly, clear, not so much functional paradigm

Less code than functions

Maybe, in pretty, functional programming, need to improve

From the text, get the dictionary [parent lib: [child lib]]

struct Parser { func parse(_ content: String) -> [String: [String]]? Struct Tag{struct pageStart = "PODS:\n" // let lineEnd: Character = "\n" // let pageEnd: String // Parent lib let itemStart = String(repeating: "", count: 2) " ", count: 4) init() { pageEnd = String(repeating: "\(lineEnd)", count: 2) } } let tag = Tag() guard content.hasPrefix(tag.pageStart) else { return nil } let info = content.rm(header: tag.pageStart) guard let temp = info.components(separatedBy: tag.pageEnd).first else{ return nil } var result = [String: [String]]() let list = temp.split(separator: Tag. LineEnd) // Add the message, Var keys = "" var vals = [String]() var started = false for item in list{let TMP = String(item) if Tmp.hasprefix (tag.subitemStart){// Sublib vals. Append (tmp.rm(header: tag.subItemStart).rmRegexHeader.regexExtract) } else if tmp.hasPrefix(tag.itemStart){ if started{ result[key] = vals } // Parent lib vals. RemoveAll () started = true key = tmp.rm(header: tag.itemStart).rmRegexHeader.regexExtract } } if result.isEmpty{ return nil } else{ return result } } }Copy the code

Helper method

Func rm(header STR: String) -> String{return String(dropFirst(str.count))} String{ if let tmp = match(regex: #"- "?" #){return String(dropFirst(tmp.count))} else{return self}} String{ if let tmp = match(regex: #"[^\s]+"#){ return tmp } else{ return self } } func match(regex: String) -> String? { guard let regex = try? NSRegularExpression(pattern: regex) else { return nil } let results = regex.matches(in: self, range: NSRange(location: 0, length: utf8.count)) let nsString = self as NSString if let first = results.first{ return nsString.substring(with: first.range) } else{ return nil } } }Copy the code

To optimize the

The following paragraph, which is not very logical,

In the for loop, the logic for the child module is processed first, and then the logic for the parent lib

var result = [String: [String]]() let list = temp.split(separator: Tag. LineEnd) // Add the message, Var keys = "" var vals = [String]() var started = false for item in list{let TMP = String(item) if Tmp.hasprefix (tag.subitemStart){// Sublib vals. Append (tmp.rm(header: tag.subItemStart).rmRegexHeader.regexExtract) } else if tmp.hasPrefix(tag.itemStart){ if started{ result[key] = vals } / lib/father vals. RemoveAll () started = true key = TMP. The rm (header: tag. ItemStart). RmRegexHeader. RegexExtract}}Copy the code

Can be adjusted to process the parent lib first, then the child lib, consistent with the actual data logic

var result = [String: [String]]() let list = temp.split(separator: tag.lineEnd) let cnt = list.count var i = 0 while i < cnt { var tmp = String(list[i]) var key: String? Var vals = [String]() lib if tmp.isitem {key = tmp.rm(header: Tag. ItemStart). Word I += 1} i < cnt { tmp = String(list[i]) if tmp.isSubitem{ vals.append(tmp.rm(header: tag.subItemStart).word) i += 1 } else{ stay = false } } if let k = key{ result[k] = vals } else{ return nil } }Copy the code

Helper method

extension String{
    
    var isItem: Bool{
        let tag = Tag()
        return hasPrefix(tag.itemStart) && (hasPrefix(tag.subItemStart) == false)
    }
    
    var isSubitem: Bool{
        let tag = Tag()
        return hasPrefix(tag.subItemStart)
    }
    
    
    var word: String{
        rmRegexHeader.regexExtract
    }
}


Copy the code

Step 2, sort out the hierarchy of the dependency graph

From dictionary [parent lib: [child lib]]

Get the auxiliary dictionary, [child lib: [parent lib]]

private func parentNode(_ dependency: [String: [String]]) -> [String: [String]] { var pMap = [String: [String]]() for (key, sons) in dependency { for name in sons { var parents = pMap[name] ?? [] parents.append(key) pMap[name] = parents // pMap[name] = key // return pMap}Copy the code

[Top libs info, Seccond Libs Info,…]

Take out the top lib that doesn’t have a parent,

Then from the rest of the lib, pull out the lib that does not have a parent, and repeat

Func groupPodDependency(_ dependency: [String: [String]]) -> [[String: [String]] {let reversed = parentNode(dependency) // Same as Set(dependency. Keys) Var names = Set(dependency. Keys) var lastDepthNames = [String]() var groups = [[String: [String]]]() while names.count > 0 { var group = [String: [String]]() let copyedNames = names // copyedNames, first round, is for each lib // later round, is the rest of which, Each lib for name in copyedNames {// first round, lastDepthNames [] // For top lib without parent lib,  // reversed[name] = [] if lastDepthNames.contains(reversed[name] ?? Group [name] = dependency[name]}} lastdepthnames. append(contentsOf: group.keys) groups.append(group) } return groups }Copy the code

Draw out

[[String: [String]]]

Each element [String: [String]] is the information for each layer

[String: [String]], the key is a layer of lib,

The values corresponding to key are the connection between the lib and its sub-lib

  • To draw it, see Github Repo

github repo