I am using th swift Charts library. I'm trying to show a candle chart but it does not correctly showing data.
After running it will be show just one item but if i will zoom in it will be show all items,
how immediately showing every item after running without needed to zoom ?
func setupCandleChart() {
// candleChartView.autoScaleMinMaxEnabled = true // if enable this doesn't show anything
setCandleDataCount(5, range: 30)
}
func setCandleDataCount(_ count: Int, range: UInt32) {
let yVals1 = (0..<count).map { (i) -> CandleChartDataEntry in
let mult = range + 1
let val = Double(arc4random_uniform(40) + mult)
let high = Double(arc4random_uniform(9) + 8)
let low = Double(arc4random_uniform(9) + 8)
let open = Double(arc4random_uniform(6) + 1)
let close = Double(arc4random_uniform(6) + 1)
let even = i % 2 == 0
return CandleChartDataEntry(x: Double(i), shadowH: val + high, shadowL: val - low, open: even ? val + open : val - open, close: even ? val - close : val + close)
}
let set1 = CandleChartDataSet(entries: yVals1, label: "Data Set")
let data = CandleChartData(dataSet: set1)
candleChartView.data = data
}
if i will zoom:
I am experiencing a problem that I am not sure how to solve and I hope someone here can help me. Currently I have a string variable and later I replace the letters in the string with underscores like the following:
var str = "Hello playground"
let replace = str.replacingOccurrences(of: "\\S", with: "_", options: .regularExpression)
print(str)
Know I would like to randomly generate 25 % of the characters in str (In this case 16 * 0,25 = 4) so it later prints something like these examples:
str = "H__l_ ___yg_____"
str = "_____ play______"
str = "__ll_ ____g____d"
Does anyone have any ideas of how to do this?
A possible solution:
var str = "Hello playground"
print("Before: \(str)")
do {
let regex = try NSRegularExpression(pattern: "\\S", options: [])
let matches = regex.matches(in: str, options: [], range: NSRange(location: 0, length: str.utf16.count))
//Retrieve 1/4 elements of the string
let randomElementsToReplace = matches.shuffled().dropLast(matches.count * 1/4)
matches.forEach({ (aMatch) in
if randomElementsToReplace.first(where: { $0.range == aMatch.range } ) != nil {
str.replaceSubrange(Range(aMatch.range, in: str)!, with: "_")
} else {
//Do nothing because that's the one we are keeping as such
}
})
print("After: \(str)")
} catch {
print("Error while creating regex: \(error)")
}
The idea behind it:
Use the same Regular Expression pattern as the one you used.
Pick up n elements in it (in your case 1/4)
Replace every character that isn't in that short list.
Now that you got the idea, it's even faster replacing the for loop with
for aMatch in randomElementsToReplace {
str.replaceSubrange(Range(aMatch.range, in: str)!, with: "_")
}
Thanks to #Martin R's comment for pointing it out.
Output (done 10 times):
$>Before: Hello playground
$>After: ____o ___y____n_
$>Before: Hello playground
$>After: _el__ _______u__
$>Before: Hello playground
$>After: _e___ ____g___n_
$>Before: Hello playground
$>After: H___o __a_______
$>Before: Hello playground
$>After: H___o _______u__
$>Before: Hello playground
$>After: __l__ _____ro___
$>Before: Hello playground
$>After: H____ p________d
$>Before: Hello playground
$>After: H_l__ _l________
$>Before: Hello playground
$>After: _____ p____r__n_
$>Before: Hello playground
$>After: H___o _____r____
$>Before: Hello playground
$>After: __l__ ___y____n_
You'll see that there is a little difference from your expected result, it's because matches.count == 15, so 1/4 of them should be what? It's up to you there to do the correct calculation according to your needs (round up?, etc.) since you didn't specified it.
Note that if you don't want to round up, you could also do the reverse, use the randomed for the one to not replace, and then the round might play in your favor.
Similarly as in Replace specific characters in string, you can map each character, and combine the result to a string. But now you have to keep track of the (remaining) numbers of non-space characters, and the (remaining) numbers of characters that should be displayed. For each (non-space) character it is randomly decided whether to display (keep) it or to replace it by an underscore.
let s = "Hello playground"
let factor = 0.25
var n = s.filter({ $0 != " " }).count // # of non-space characters
var m = lrint(factor * Double(n)) // # of characters to display
let t = String(s.map { c -> Character in
if c == " " {
// Preserve space
return " "
} else if Int.random(in: 0..<n) < m {
// Keep
m -= 1
n -= 1
return c
} else {
// Replace
n -= 1
return "_"
}
})
print(t) // _e_l_ ______o_n_
This method creates an array of bools that determines which characters will be kept and which will be replaced by using the inbuilt shuffled function.
let string = "Hello playground"
let charsToKeep = string.count / 4
let bools = (Array<Bool>(repeating: true, count: charsToKeep)
+ Array<Bool>(repeating: false, count: string.count - charsToKeep)).shuffled()
let output = zip(string, bools).map
{
char, bool in
return bool ? char : "_"
}
print(String(output))
Edit The above doesn't deal with spaces correctly, but I'll leave it here anyway as a general example.
Here is a version that does deal with the spaces.
let string = "Hello playground and stackoverflow"
let nonSpaces = string.filter{ $0 != " " }.count
let bools = (Array<Bool>(repeating: true, count: nonSpaces / 4) + Array<Bool>(repeating: false, count: nonSpaces - nonSpaces / 4)).shuffled()
var nextBool = bools.makeIterator()
let output = string.map
{
char in
return char == " " ? " " : (nextBool.next()! ? char : "_")
}
print(String(output))
// Hel__ __________ a__ __a____e____w
// ___l_ _l__g_____ _n_ __a_____r__o_
Another possible approach is to generate random indexes for the given string and then replace the characters at those indexes:
var str = "Hello, playground"
let indexes: [Int] = Array(0..<str.count)
let randomIndexes = Array(indexes.shuffled()[0..<(str.count / 4)])
for index in randomIndexes {
let start = str.index(str.startIndex, offsetBy: index)
let end = str.index(str.startIndex, offsetBy: index+1)
str.replaceSubrange(start..<end, with: "_")
}
print(str)
If you put this in a extension on String, it would look like:
extension String {
func randomUnderscores(factor: Double) -> String {
let indexes: [Int] = Array(0..<count)
let endIndexes = Int(Double(count) * factor)
let randomIndexes = Array(indexes.shuffled()[0..<endIndexes])
var randomized = self
for index in randomIndexes {
let start = randomized.index(startIndex, offsetBy: index)
let end = randomized.index(startIndex, offsetBy: index+1)
randomized.replaceSubrange(start..<end, with: "_")
}
return randomized
}
}
print(str.randomUnderscores(factor: 0.25))
I just came up with the following solution:
func generateMyString(string: String) -> String {
let percentage = 0.25
let numberOfCharsToReplace = Int(floor(Double(string.count) * percentage))
let generatedString = stride(from: 0, to: string.count, by: 1).map { index -> String in
return string[string.index(string.startIndex, offsetBy: index)] == " " ? " " : "_"
}.joined()
var newString = generatedString
for i in generateNumbers(repetitions: numberOfCharsToReplace, maxValue: string.count - 1) {
var newStringArray = Array(newString)
newStringArray[i] = Array(string)[i]
newString = String(newStringArray)
}
return newString
}
func generateNumbers(repetitions: Int, maxValue: Int) -> [Int] {
guard maxValue >= repetitions else {
fatalError("maxValue must be >= repetitions for the numbers to be unique")
}
var numbers = [Int]()
for _ in 0..<repetitions {
var n: Int
repeat {
n = Int.random(in: 1...maxValue)
} while numbers.contains(n)
numbers.append(n)
}
return numbers
}
Output:
let str = "Hello playground"
print(generateMyString(string: str)) // ___lo _l_______d
A solution that keeps whitespaces and punctation intact.
We will find them with an extension method indiciesOfPuntationBlanks() -> [Int]. replacing the randomly picked chars will be done by blankOut(percentage: Double) -> String
extension String {
func indiciesOfPuntationBlanks() -> [Int] {
let charSet = CharacterSet.punctuationCharacters.union(.whitespaces)
var indices = [Int]()
var searchStartIndex = self.startIndex
while searchStartIndex < self.endIndex,
let range = self.rangeOfCharacter(from: charSet, options: [], range: searchStartIndex ..< self.endIndex),
!range.isEmpty
{
let index = distance(from: self.startIndex, to: range.lowerBound)
indices.append(index)
searchStartIndex = range.upperBound
}
return indices
}
func blankOut(percentage: Double) -> String {
var result = self
let blankIndicies = result.indiciesOfPuntationBlanks()
let allNonBlankIndicies = Set(0 ..< result.count).subtracting(blankIndicies).shuffled()
let picked = allNonBlankIndicies.prefix(Int(Double(allNonBlankIndicies.count) * percentage))
picked.forEach { (idx) in
let start = result.index(result.startIndex, offsetBy: idx);
let end = result.index(result.startIndex, offsetBy: idx + 1);
result.replaceSubrange(start ..< end, with: "_")
}
return result
}
}
Usage:
let str = "Hello, World!"
for _ in 0 ..< 10 {
print(str.blankOut(percentage: 0.75))
}
Output:
____o, _or__!
_e___, __rl_!
_e__o, __r__!
H____, W_r__!
H_l__, W____!
_____, _or_d!
_e_lo, _____!
_____, _orl_!
_____, _or_d!
___l_, W___d!
Same solution but the string for blanking out and the character sets to be ignored can be configured
extension String {
func indicies(with charSets:[CharacterSet]) -> [Int] {
var indices = [Int]()
let combinedCahrSet: CharacterSet = charSets.reduce(.init()) { $0.union($1) }
var searchStartIndex = self.startIndex
while searchStartIndex < self.endIndex,
let range = self.rangeOfCharacter(from: combinedCahrSet, options: [], range: searchStartIndex ..< self.endIndex),
!range.isEmpty
{
let index = distance(from: self.startIndex, to: range.lowerBound)
indices.append(index)
searchStartIndex = range.upperBound
}
return indices
}
func blankOut(percentage: Double, with blankOutString: String = "_", ignore charSets: [CharacterSet] = [.punctuationCharacters, .whitespaces]) -> String {
var result = self
let blankIndicies = result.indicies(with: charSets)
let allNonBlankIndicies = Set(0 ..< result.count).subtracting(blankIndicies).shuffled()
let picked = allNonBlankIndicies.prefix(Int(Double(allNonBlankIndicies.count) * percentage))
picked.forEach { (idx) in
let start = result.index(result.startIndex, offsetBy: idx);
let end = result.index(result.startIndex, offsetBy: idx + 1);
result.replaceSubrange(start ..< end, with: blankOutString)
}
return result
}
}
Usage:
let str = "Hello, World!"
for _ in 0 ..< 10 {
print(str.blankOut(percentage: 0.75))
}
print("--------------------")
for _ in 0 ..< 10 {
print(str.blankOut(percentage: 0.75, with:"x", ignore: [.punctuationCharacters]))
}
print("--------------------")
for _ in 0 ..< 10 {
print(str.blankOut(percentage: 0.75, with:"*", ignore: []))
}
Output:
_el_o, _____!
__llo, _____!
He__o, _____!
_e___, W_r__!
_el_o, _____!
_el__, ___l_!
_e___, __rl_!
_e__o, _o___!
H____, Wo___!
H____, __rl_!
--------------------
xxxlx,xWxrxx!
xxxxx,xxorxd!
Hxxxx,xWxrxx!
xxxxx, xoxlx!
Hxllx,xxxxxx!
xelxx,xxoxxx!
Hxxxx,xWxxxd!
Hxxxo,xxxxxd!
Hxxxx,xxorxx!
Hxxxx, Wxxxx!
--------------------
***l***Wo**d*
*e**o**W**l**
***lo**Wo****
*el*****or***
H****,****ld*
***l*, **r***
*el*o* ******
*e*lo*******!
H*l****W***d*
H****, **r***
You can use a 3-steps algorithm that does the following:
builds the list of all non-space indices
removes the first 25% random elements from that list
go through all characters and replace all whose index is part of list from #2, by an underscore
The code could look something like this:
func underscorize(_ str: String, factor: Double) -> String {
// making sure we have a factor between 0 and 1
let factor = max(0, min(1, factor))
let nonSpaceIndices = str.enumerated().compactMap { $0.1 == " " ? nil : $0.0 }
let replaceIndices = nonSpaceIndices.shuffled().dropFirst(Int(Double(str.count) * factor))
return String(str.enumerated().map { replaceIndices.contains($0.0) ? "_" : $0.1 })
}
let str = "Hello playground"
print(underscorize(str, factor: 0.25))
Sample results:
____o p_ay______
____o p__y____n_
_el_o p_________
The idea is same as above methods, just with a little less code.
var str = "Hello playground"
print(randomString(str))
print(randomString(str))
// counting whitespace as a random factor
func randomString(_ str: String) -> String{
let strlen = str.count
let effectiveCount = Int(Double(strlen) * 0.25)
let shuffled = (0..<strlen).shuffled()
return String(str.enumerated().map{
shuffled[$0.0] < effectiveCount || ($0.1) == " " ? ($0.1) : "_"
})}
//___l_ _l__gr____
//H____ p___g____d
func underscorize(_ str: String) -> String{
let effectiveStrlen = str.filter{$0 != " "}.count
let effectiveCount = Int(floor(Double(effectiveStrlen) * 0.25))
let shuffled = (0..<effectiveStrlen).shuffled()
return String((str.reduce(into: ([],0)) {
$0.0.append(shuffled[$0.1] <= effectiveCount || $1 == " " ? $1 : "_" )
$0.1 += ($1 == " ") ? 0 : 1}).0)
}
print(underscorize(str))
print(underscorize(str))
//__l__ pl__g_____
//___lo _l_______d
First you need to get the indices of your string and filter the ones that are letters. Then you can shuffle the result and pick the number of elements (%) minus the number of spaces in the original string, iterate through the result replacing the resulting ranges with the underscore.
You can extending RangeReplaceable protocol to be able to use it with substrings as well:
extension StringProtocol where Self: RangeReplaceableCollection{
mutating func randomReplace(characterSet: CharacterSet = .letters, percentage: Double, with element: Element = "_") {
precondition(0...1 ~= percentage)
let indices = self.indices.filter {
characterSet.contains(self[$0].unicodeScalars.first!)
}
let lettersCount = indices.count
let nonLettersCount = count - lettersCount
let n = lettersCount - nonLettersCount - Int(Double(lettersCount) * Double(1-percentage))
indices
.shuffled()
.prefix(n)
.forEach {
replaceSubrange($0...$0, with: Self([element]))
}
}
func randomReplacing(characterSet: CharacterSet = .letters, percentage: Double, with element: Element = "_") -> Self {
precondition(0...1 ~= percentage)
var result = self
result.randomReplace(characterSet: characterSet, percentage: percentage, with: element)
return result
}
}
// mutating test
var str = "Hello playground"
str.randomReplace(percentage: 0.75) // "___lo _l___r____\n"
print(str) // "___lo _l___r____\n"
// non mutating with another character
let str2 = "Hello playground"
str2.randomReplacing(percentage: 0.75, with: "•") // "••••o p••y•••u••"
print(str2) // "Hello playground\n"
i am making an app in swift 4 and i want the enetered number label text to show something like this when otp is sent
" otp is sent to +91******21 "
here is the thing I found now I don't own what logic should be applied here to post string like that
var mobileNumer = "+91987654321"
let intLetters = mobileNumer.prefix(3)
let endLetters = mobileNumer.suffix(2)
i want this tpe of number to be shown on the label after enytering the mobile number , it should show frist two numbers then start and hen show last two numbers
try this:
var mobileNumer = "+91987654321"
let intLetters = mobileNumer.prefix(3)
let endLetters = mobileNumer.suffix(2)
let newString = intLetters + "*******" + endLetters //"+91*******21"
Or if you want to be safe:
var mobileNumer = "+91987654321"
guard mobileNumer.count > 5 else {
fatalError("The phone number is not complete")
}
let intLetters = mobileNumer.prefix(3)
let endLetters = mobileNumer.suffix(2)
let stars = String(repeating: "*", count: mobileNumer.count - 5)
let result = intLetters + stars + endLetters
Or if you'd prefer to replace a subrange:
var mobileNumer = "+91987654321"
guard mobileNumer.count > 5 else {
fatalError("The phone number is not complete")
}
let startingIndex = mobileNumer.index(mobileNumer.startIndex, offsetBy: 3)
let endingIndex = mobileNumer.index(mobileNumer.endIndex, offsetBy: -2)
let stars = String(repeating: "*", count: mobileNumer.count - 5)
let result = mobileNumer.replacingCharacters(in: startingIndex..<endingIndex,
with: stars)
Or
If you'd like to mutate mobileNumer:
mobileNumer.replaceSubrange(startingIndex..<endingIndex, with: stars)
print(mobileNumer) //"+91*******21"
You can use this function.
func starifyNumber(number: String) -> String {
let intLetters = number.prefix(3)
let endLetters = number.suffix(2)
let numberOfStars = number.count - (intLetters.count + endLetters.count)
var starString = ""
for _ in 1...numberOfStars {
starString += "*"
}
let finalNumberToShow: String = intLetters + starString + endLetters
return finalNumberToShow
}
To call it
let mobileNumer = starifyNumber(number: "+91987654321")
print(mobileNumer) \\+91*******21
How can I print a binary tree in Swift so that the input 79561 prints output like this:
7
/ \
5 9
/ \
1 6
I tried to arrange this with some code using For Loops and If Statements but it didn't worked.
My code is:
import UIKit
//Variable "node" used only to arrange it in output.
var node = "0"
var space = " "
var linkLeft = "/"
var linkRight = "\\"
var str = "Hello, playground"
var height = 6
var width = height * 2 + 1
print()
//Height
for h in 1...height {
//Width
for w in 1...width {
switch h {
case 1:
if(w == width/2 + h) {
print(node, terminator: "")
} else {
print(space, terminator: "")
}
if (w == width) {
print()
}
case 2:
//print(linkLeft, terminator: "")
if(w == width/3 + h) {
print(linkLeft, terminator: "")
} else if(w == width/3 + h + 4) {
print(linkRight, terminator: "")
} else {
print(space, terminator: "")
}
if (w == width) {
print()
}
case 3:
if(w == width/5 + h) {
print(node, terminator: "")
} else if(w == width/h + h){
print(node, terminator: "")
} else {
print(space, terminator: "")
}
if (w == width) {
print()
}
break
default:
break
}
}
}
I tried to use two For Loops one for height and other one for width. But it's not working if number of nodes changes. For now I just tried to arrange places of links (/ and \), nodes and spaces, so it's not working.
Is there a possible way to do this ?
First you have to define a hierarchical tree structure (class) that allows recursive traversal of the tree nodes. How you implement it doesn't matter as long as it can provide a descriptive string and access to its left and right sub nodes.
For example (I used this for testing purposes):
class TreeNode
{
var value : Int
var left : TreeNode? = nil
var right : TreeNode? = nil
init(_ rootValue:Int)
{ value = rootValue }
#discardableResult
func addValue( _ newValue:Int) -> TreeNode
{
if newValue == value // exclude duplicate entries
{ return self }
else if newValue < value
{
if let newNode = left?.addValue(newValue)
{ return newNode }
left = TreeNode(newValue)
return left!
}
else
{
if let newNode = right?.addValue(newValue)
{ return newNode }
right = TreeNode(newValue)
return right!
}
}
}
Then you can create a recursive function to obtain the lines to print. Each line will need to be aware of lower level lines so the list of lines needs to be built from the bottom up. Recursion is an easy way to achieve this kind of interdependency.
Here's an example of a generic function that will work for any binary tree class. It expects a root node and a function (or closure) to access a node's description and left/right sub nodes :
public func treeString<T>(_ node:T, reversed:Bool=false, isTop:Bool=true, using nodeInfo:(T)->(String,T?,T?)) -> String
{
// node value string and sub nodes
let (stringValue, leftNode, rightNode) = nodeInfo(node)
let stringValueWidth = stringValue.count
// recurse to sub nodes to obtain line blocks on left and right
let leftTextBlock = leftNode == nil ? []
: treeString(leftNode!,reversed:reversed,isTop:false,using:nodeInfo)
.components(separatedBy:"\n")
let rightTextBlock = rightNode == nil ? []
: treeString(rightNode!,reversed:reversed,isTop:false,using:nodeInfo)
.components(separatedBy:"\n")
// count common and maximum number of sub node lines
let commonLines = min(leftTextBlock.count,rightTextBlock.count)
let subLevelLines = max(rightTextBlock.count,leftTextBlock.count)
// extend lines on shallower side to get same number of lines on both sides
let leftSubLines = leftTextBlock
+ Array(repeating:"", count: subLevelLines-leftTextBlock.count)
let rightSubLines = rightTextBlock
+ Array(repeating:"", count: subLevelLines-rightTextBlock.count)
// compute location of value or link bar for all left and right sub nodes
// * left node's value ends at line's width
// * right node's value starts after initial spaces
let leftLineWidths = leftSubLines.map{$0.count}
let rightLineIndents = rightSubLines.map{$0.prefix{$0==" "}.count }
// top line value locations, will be used to determine position of current node & link bars
let firstLeftWidth = leftLineWidths.first ?? 0
let firstRightIndent = rightLineIndents.first ?? 0
// width of sub node link under node value (i.e. with slashes if any)
// aims to center link bars under the value if value is wide enough
//
// ValueLine: v vv vvvvvv vvvvv
// LinkLine: / \ / \ / \ / \
//
let linkSpacing = min(stringValueWidth, 2 - stringValueWidth % 2)
let leftLinkBar = leftNode == nil ? 0 : 1
let rightLinkBar = rightNode == nil ? 0 : 1
let minLinkWidth = leftLinkBar + linkSpacing + rightLinkBar
let valueOffset = (stringValueWidth - linkSpacing) / 2
// find optimal position for right side top node
// * must allow room for link bars above and between left and right top nodes
// * must not overlap lower level nodes on any given line (allow gap of minSpacing)
// * can be offset to the left if lower subNodes of right node
// have no overlap with subNodes of left node
let minSpacing = 2
let rightNodePosition = zip(leftLineWidths,rightLineIndents[0..<commonLines])
.reduce(firstLeftWidth + minLinkWidth)
{ max($0, $1.0 + minSpacing + firstRightIndent - $1.1) }
// extend basic link bars (slashes) with underlines to reach left and right
// top nodes.
//
// vvvvv
// __/ \__
// L R
//
let linkExtraWidth = max(0, rightNodePosition - firstLeftWidth - minLinkWidth )
let rightLinkExtra = linkExtraWidth / 2
let leftLinkExtra = linkExtraWidth - rightLinkExtra
// build value line taking into account left indent and link bar extension (on left side)
let valueIndent = max(0, firstLeftWidth + leftLinkExtra + leftLinkBar - valueOffset)
let valueLine = String(repeating:" ", count:max(0,valueIndent))
+ stringValue
let slash = reversed ? "\\" : "/"
let backSlash = reversed ? "/" : "\\"
let uLine = reversed ? "¯" : "_"
// build left side of link line
let leftLink = leftNode == nil ? ""
: String(repeating: " ", count:firstLeftWidth)
+ String(repeating: uLine, count:leftLinkExtra)
+ slash
// build right side of link line (includes blank spaces under top node value)
let rightLinkOffset = linkSpacing + valueOffset * (1 - leftLinkBar)
let rightLink = rightNode == nil ? ""
: String(repeating: " ", count:rightLinkOffset)
+ backSlash
+ String(repeating: uLine, count:rightLinkExtra)
// full link line (will be empty if there are no sub nodes)
let linkLine = leftLink + rightLink
// will need to offset left side lines if right side sub nodes extend beyond left margin
// can happen if left subtree is shorter (in height) than right side subtree
let leftIndentWidth = max(0,firstRightIndent - rightNodePosition)
let leftIndent = String(repeating:" ", count:leftIndentWidth)
let indentedLeftLines = leftSubLines.map{ $0.isEmpty ? $0 : (leftIndent + $0) }
// compute distance between left and right sublines based on their value position
// can be negative if leading spaces need to be removed from right side
let mergeOffsets = indentedLeftLines
.map{$0.count}
.map{leftIndentWidth + rightNodePosition - firstRightIndent - $0 }
.enumerated()
.map{ rightSubLines[$0].isEmpty ? 0 : $1 }
// combine left and right lines using computed offsets
// * indented left sub lines
// * spaces between left and right lines
// * right sub line with extra leading blanks removed.
let mergedSubLines = zip(mergeOffsets.enumerated(),indentedLeftLines)
.map{ ( $0.0, $0.1, $1 + String(repeating:" ", count:max(0,$0.1)) ) }
.map{ $2 + String(rightSubLines[$0].dropFirst(max(0,-$1))) }
// Assemble final result combining
// * node value string
// * link line (if any)
// * merged lines from left and right sub trees (if any)
let treeLines = [leftIndent + valueLine]
+ (linkLine.isEmpty ? [] : [leftIndent + linkLine])
+ mergedSubLines
return (reversed && isTop ? treeLines.reversed(): treeLines)
.joined(separator:"\n")
}
To actually print, you'll need to supply the function with your class's node and a closure to access node descriptions and the left and right sub nodes.
extension TreeNode
{
var asString:String { return treeString(self){("\($0.value)",$0.left,$0.right)} }
}
var root = TreeNode(7)
root.addValue(9)
root.addValue(5)
root.addValue(6)
root.addValue(1)
print(root.asString)
// 7
// / \
// 5 9
// / \
// 1 6
//
root = TreeNode(80)
root.addValue(50)
root.addValue(90)
root.addValue(10)
root.addValue(60)
root.addValue(30)
root.addValue(70)
root.addValue(55)
root.addValue(5)
root.addValue(35)
root.addValue(85)
print(root.asString)
// 80
// ___/ \___
// 50 90
// __/ \__ /
// 10 60 85
// / \ / \
// 5 30 55 70
// \
// 35
//
[EDIT] Improved logic to use less space on trees with deeper right side than left. Cleaned up code and added comments to explain how it works.
//
// 12
// / \
// 10 50
// / __/ \__
// 5 30 90
// \ /
// 35 70
// / \
// 60 85
// /
// 55
//
// 12
// / \
// 10 30
// / \
// 5 90
// /
// 85
// /
// 70
// /
// 55
// /
// 48
// /
// 45
// /
// 40
// /
// 35
//
[EDIT2] made the function generic and adapted explanations.
With the generic function, the data doesn't even need to be in an actual tree structure.
For example, you could print an array containing a heap tree:
extension Array
{
func printHeapTree(reversed:Bool = false)
{
let tree = treeString( 0, reversed:reversed )
{
let left = { $0 < self.count ? $0 : nil}($0 * 2 + 1)
let right = { $0 < self.count ? $0 : nil}($0 * 2 + 2)
return ( "\(self[$0])", left, right )
}
print(tree)
}
}
let values = [7,5,9,1,6]
values.printHeapTree()
// 7
// / \
// 5 9
// / \
// 1 6
let family = [ "Me","Paul","Rosa","Vincent","Jody","John","Kate"]
family.printHeapTree()
// Me
// ___/ \___
// Paul Rosa
// / \ / \
// Vincent Jody John Kate
But for that last example, a family tree is usually presented upside down. So, I adjusted the function to allow printing a reversed tree:
family.printHeapTree(reversed:true)
// Vincent Jody John Kate
// \ / \ /
// Paul Rosa
// ¯¯¯\ /¯¯¯
// Me
[EDIT3] Added condition to exclude duplicate entries from tree in example class (TreeNode) per Emm's request
[EDIT4] changed formula for mergedSubLines so that it will compile in an actual project (was testing this in the playground).
[EDIT5] minor adjustments for Swift4, added ability to print a reversed tree, changed Array example to a heap tree.
My easy way that I found and modified a bit for swift
import UIKit
// Binary tree by class
class TreeNode
{
var value : Int
var left : TreeNode? = nil
var right : TreeNode? = nil
init(_ rootValue:Int) {
value = rootValue
}
#discardableResult
func addValue( _ newValue:Int) -> TreeNode
{
if newValue == value {
return self
}
else if newValue < value {
if let newNode = left?.addValue(newValue) {
return newNode
}
left = TreeNode(newValue)
return left!
}
else {
if let newNode = right?.addValue(newValue) {
return newNode
}
right = TreeNode(newValue)
return right!
}
}
func myPrint () {
printTree(prefix: "", n: self, isLeft: false)
}
}
func printTree(prefix: String, n: TreeNode, isLeft: Bool) {
print(prefix, (isLeft ? "|-- " : "\\-- "), n.value)
if n.left != nil {
printTree(prefix: "\(prefix) \(isLeft ? "| " : " ") ", n: n.left!, isLeft: true)
}
if n.right != nil {
printTree(prefix: "\(prefix) \(isLeft ? "| " : " ") ", n: n.right!, isLeft: false)
}
}
Input
var root = TreeNode(80)
root.addValue(50)
root.addValue(90)
root.addValue(10)
root.addValue(60)
root.addValue(30)
root.addValue(70)
root.addValue(55)
root.addValue(5)
root.addValue(35)
root.addValue(85)
root.addValue(84)
root.addValue(86)
root.addValue(92)
root.addValue(100)
root.myPrint()
Output
\-- 80
|-- 50
| |-- 10
| | |-- 5
| | \-- 30
| | \-- 35
| \-- 60
| |-- 55
| \-- 70
\-- 90
|-- 85
| |-- 84
| \-- 86
\-- 92
\-- 100
I'm trying to use perlin noise to generate terain using Unity3D (version 5.0.2f1) but I get this error:
InvalidProgramException: Invalid IL code in TerrainGenerator:GenerateFloor (): IL_0045: call 0x0a00000c
TerrainGenerator.Generate () (at Assets/TerrainGenerator.js:36)
TerrainGenerator.Start () (at Assets/TerrainGenerator.js:23)
Code:
function GenerateFloor(){
print("The function was called");
if(!(seed > 0)){
Debug.LogError("Seed not valid. Seed: " + seed + " .");
seed = Random.Range(0, 1000000000000000);
Debug.LogError("Generated new seed. Seed: " + seed + ".");
}
for(var i = 0; i < heightMap.length; i++){
if(currentX == Math.Sqrt(size)){
currentX = 0;
currentZ++;
}
else if(currentX > Math.Sqrt(size)) Debug.LogError("How did this happen?! currentX = " + currentX + " size = " + size + " .");
var height = Mathf.PerlinNoise(currentX, currentZ);
heightMap[currentX * currentZ] = new Vector3(currentX, height, currentZ);
print("For loop worked");
//yield;
}
}
Your param 1000000000000000 in Random.Range is excessively beyond maximum. Maybe you want int.MaxValue ? which would be 2147483647
Interesting it caused such a compiler error with IL2CPP !