I'm struggling with charts (I use the wonderful Charts library). I had another problem, the solution of which I could not find.
I have a horizontal chart, everything is relatively fine with it, but I need the new bar that I add to be of a different color and with a new label on the y-axis (in a horizontal chart, this is the x-axis).
This is what my chart looks like
This is what I need to get
This is the code where you can see how I add the bar
let barWidth = 5.3
let spaceForBar = 10.3
var n = 0
var yVals = (0..<count).map { (i) -> BarChartDataEntry in
let val = items.map{Double($0.iqLevel)}[n]
n += 1
return BarChartDataEntry(x: Double(i)*spaceForBar, y: val)
}
let userData = testResultItem(group: "New user", iqLevel: 118).iqLevel
let newEntry = BarChartDataEntry(x: 2.5*spaceForBar, y: Double(userData))
yVals.append(newEntry)
let chartDataSet = BarChartDataSet(entries: yVals)
I would be immensely grateful to everyone who can help at least some advice!
You can create a separate BarChartDataSet for this single user entry and set a different color for it.
let yVals = (0..<count).map { (i) -> BarChartDataEntry in
let val = items.map{Double($0.iqLevel)}[n]
n += 1
return BarChartDataEntry(x: Double(i)*spaceForBar, y: val)
}
let userData = testResultItem(group: "New user", iqLevel: 118).iqLevel
let userEntry = BarChartDataEntry(x: 2.5*spaceForBar, y: Double(userData))
let chartDataSet = BarChartDataSet(entries: yVals)
chartDataSet.setColor(YourCurrentColor)
let userDataSet = BarChartDataSet(entries: [userEntry])
userDataSet.setColor(DarkGreenColor) //For user entry
let barChartData = BarChartData(dataSets: [chartDataSet, userDataSet])
I'm running macOS on my MacBook Pro (Retina, 15-inch, Mid 2015), which has two GPUs in it, according to "About this Mac" in the Apple menu. One GPU is an AMD Radeon R9 M370X 2 GB, the other is an Intel Iris Pro 1536 MB -- the standard chips, I guess? They're the chips that were in there when I bought it, nothing I added myself.
I'm using the Swift MPS library for matrix computations; it works great on the Intel GPU, but when I select the Radeon, I only ever get back zeros from every operation, with no error reported. I've looked around for documentation on it, but I can't find anything. The only clue I have so far is that the Radeon reports "not integrated" (or at least, I think it does, based on the sample code at Finding GPUs on macOS, which is about as useful as Apple's doc ever is, meaning not very). If I've read that page correctly, this is what my two GPUs are telling me.
Device Intel Iris Pro Graphics; caps: headful, not discrete, integrated, not external
Device AMD Radeon R9 M370X; caps: headful, discrete, not integrated, not external
I can't find any doc that would suggest what I'm doing wrong. I've been all over Apple's MPS documentation, to no avail. And as I say, the code works great on the Intel GPU, so I should think it would run on the Radeon too. I've run some downloadable diagnostic tools to check on the Radeon, but it doesn't show up in the menus of those tools. So I don't even know whether this is something I'm doing wrong in the code, or if the chip itself is broken.
Below is the code, which you can build as a console app by pasting into main.swift. Find the following line:
let device = MTLCopyAllDevices()[1]
I use [0] for the Intel, [1] for the Radeon, and you can see that the output is different, that is, all zeros for the Radeon. I suppose your mileage may vary depending on your machine. I welcome any input, cheers
import MetalPerformanceShaders
typealias MPSNumber = Float32
let MPSNumberSize = MemoryLayout<MPSNumber>.size
let MPSNumberTypeInGPU = MPSDataType.float32
class MPSNet {
let commandBuffer: MTLCommandBuffer
let commandQueue: MTLCommandQueue
let device = MTLCopyAllDevices()[1]
var neuronsInMatrix1: MPSMatrix?
var neuronsInMatrix2: MPSMatrix?
var neuronsOutMatrix: MPSMatrix?
init() {
guard let cq = device.makeCommandQueue() else { fatalError() }
guard let cb = cq.makeCommandBuffer() else { fatalError() }
commandQueue = cq
commandBuffer = cb
let cMatrices = 2
let cRows = 1
let cColumns = 3
let sensoryInputs1: [MPSNumber] = [1, 2, 3]
let sensoryInputs2: [MPSNumber] = [4, 5, 6]
neuronsInMatrix1 = makeMatrix(device, sensoryInputs1)
neuronsInMatrix2 = makeMatrix(device, sensoryInputs2)
let rowStride = MPSMatrixDescriptor.rowBytes(fromColumns: cColumns, dataType: MPSNumberTypeInGPU)
neuronsOutMatrix = makeMatrix(device, cRows, cColumnsOut: cColumns, rowStride: rowStride)
let adder = MPSMatrixSum(
device: device, count: cMatrices, rows: cRows, columns: cColumns, transpose: false
)
adder.encode(
to: commandBuffer,
sourceMatrices: [neuronsInMatrix1!, neuronsInMatrix2!],
resultMatrix: neuronsOutMatrix!, scale: nil, offsetVector: nil,
biasVector: nil, start: 0
)
commandBuffer.addCompletedHandler { _ in
let motorOutputs = self.getComputeOutput(self.neuronsOutMatrix!)
let discrete = !self.device.isLowPower && !self.device.isRemovable
let caps = "\(self.device.isHeadless ? " headless" : " headful")" +
"\(discrete ? ", discrete" : ", not discrete")" +
"\(self.device.isLowPower ? ", integrated" : ", not integrated")" +
"\(self.device.isRemovable ? ", external" : ", not external")"
print("Device \(self.device.name); caps:\(caps); motor outputs \(motorOutputs)")
}
}
func compute() {
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
}
}
extension MPSNet {
func getComputeOutput(_ matrix: MPSMatrix) -> [Double] {
let rc = matrix.data.contents()
return stride(from: 0, to: matrix.columns * MPSNumberSize, by: MPSNumberSize).map {
offset in
let rr = rc.load(fromByteOffset: offset, as: MPSNumber.self)
return Double(rr)
}
}
func loadMatrix(_ data: MTLBuffer, _ rawValues: [MPSNumber]) {
let dContents = data.contents()
zip(stride(from: 0, to: rawValues.count * MPSNumberSize, by: MPSNumberSize), rawValues).forEach { z in
let (byteOffset, rawValue) = (z.0, MPSNumber(z.1))
dContents.storeBytes(of: rawValue, toByteOffset: byteOffset, as: MPSNumber.self)
}
}
func makeMatrix(_ device: MTLDevice, _ rawValues: [MPSNumber]) -> MPSMatrix {
let rowStride = MPSMatrixDescriptor.rowBytes(
fromColumns: rawValues.count, dataType: MPSNumberTypeInGPU
)
let descriptor = MPSMatrixDescriptor(
dimensions: 1, columns: rawValues.count, rowBytes: rowStride,
dataType: MPSNumberTypeInGPU
)
guard let inputBuffer = device.makeBuffer(
length: descriptor.matrixBytes, options: MTLResourceOptions.storageModeManaged
) else { fatalError() }
loadMatrix(inputBuffer, rawValues)
return MPSMatrix(buffer: inputBuffer, descriptor: descriptor)
}
func makeMatrix(_ device: MTLDevice, _ cRowsOut: Int, cColumnsOut: Int, rowStride: Int) -> MPSMatrix {
let matrixDescriptor = MPSMatrixDescriptor(
dimensions: cRowsOut, columns: cColumnsOut,
rowBytes: rowStride, dataType: MPSNumberTypeInGPU
)
return MPSMatrix(device: device, descriptor: matrixDescriptor)
}
}
let net = MPSNet()
net.compute()
It appears you have failed to use -[MPSMatrix synchronizeOnCommandBuffer:]. On discrete devices, some explicit synchronization is required before the data will come back from the GPU.
The problem lies in the storage mode of your matrix buffers. You're using MTLResourceOptions.storageModeManaged, which tells Metal that you want to manage synchronization of the memory being shared between the CPU and GPU. As mentioned in another answer here, you must use MPSMatrix.synchronize(on: MTLCommandBuffer) after a GPU operation before you try to read the data with the CPU. But you must also synchronize in the other direction, ie, after CPU operations before you commit the command to the GPU, using MTLBuffer.didModifyRange(_: Range).
Alternatively, you can use the shared storage mode, MTLResourceOptions.storageModeShared, which takes care of the synchronization for you.
See Synchronizing a Managed Resource in the Apple doc for details.
Below is a working version of your example using the managed storage mode as you have. Notice the differences in function MPSNet.compute(). You can leave that stuff out and just change the storage mode when creating the MTLBuffers for your matrices if your app is ok to use the shared storage mode.
import MetalPerformanceShaders
typealias MPSNumber = Float32
let MPSNumberSize = MemoryLayout<MPSNumber>.size
let MPSNumberTypeInGPU = MPSDataType.float32
class MPSNet {
let commandBuffer: MTLCommandBuffer
let commandQueue: MTLCommandQueue
let device = MTLCopyAllDevices()[1]
var neuronsInMatrix1: MPSMatrix?
var neuronsInMatrix2: MPSMatrix?
var neuronsOutMatrix: MPSMatrix?
init() {
guard let cq = device.makeCommandQueue() else { fatalError() }
guard let cb = cq.makeCommandBuffer() else { fatalError() }
commandQueue = cq
commandBuffer = cb
let cMatrices = 2
let cRows = 1
let cColumns = 3
let sensoryInputs1: [MPSNumber] = [1, 2, 3]
let sensoryInputs2: [MPSNumber] = [4, 5, 6]
neuronsInMatrix1 = makeMatrix(device, sensoryInputs1)
neuronsInMatrix2 = makeMatrix(device, sensoryInputs2)
let rowStride = MPSMatrixDescriptor.rowBytes(fromColumns: cColumns, dataType: MPSNumberTypeInGPU)
neuronsOutMatrix = makeMatrix(device, cRows, cColumnsOut: cColumns, rowStride: rowStride)
let adder = MPSMatrixSum(
device: device, count: cMatrices, rows: cRows, columns: cColumns, transpose: false
)
adder.encode(
to: commandBuffer,
sourceMatrices: [neuronsInMatrix1!, neuronsInMatrix2!],
resultMatrix: neuronsOutMatrix!, scale: nil, offsetVector: nil,
biasVector: nil, start: 0
)
commandBuffer.addCompletedHandler { _ in
let motorOutputs = self.getComputeOutput(self.neuronsOutMatrix!)
let discrete = !self.device.isLowPower && !self.device.isRemovable
let caps = "\(self.device.isHeadless ? " headless" : " headful")" +
"\(discrete ? ", discrete" : ", not discrete")" +
"\(self.device.isLowPower ? ", integrated" : ", not integrated")" +
"\(self.device.isRemovable ? ", external" : ", not external")"
print("Device \(self.device.name); caps:\(caps); motor outputs \(motorOutputs)")
}
}
func compute() {
for matrix in [neuronsInMatrix1!, neuronsInMatrix2!, neuronsOutMatrix!] {
let matrixData = matrix.data
matrixData.didModifyRange(0..<matrixData.length)
matrix.synchronize(on: commandBuffer)
}
commandBuffer.commit()
}
}
extension MPSNet {
func getComputeOutput(_ matrix: MPSMatrix) -> [Double] {
let rc = matrix.data.contents()
return stride(from: 0, to: matrix.columns * MPSNumberSize, by: MPSNumberSize).map {
offset in
let rr = rc.load(fromByteOffset: offset, as: MPSNumber.self)
return Double(rr)
}
}
func loadMatrix(_ data: MTLBuffer, _ rawValues: [MPSNumber]) {
let dContents = data.contents()
zip(stride(from: 0, to: rawValues.count * MPSNumberSize, by: MPSNumberSize), rawValues).forEach { z in
let (byteOffset, rawValue) = (z.0, MPSNumber(z.1))
dContents.storeBytes(of: rawValue, toByteOffset: byteOffset, as: MPSNumber.self)
}
}
func makeMatrix(_ device: MTLDevice, _ rawValues: [MPSNumber]) -> MPSMatrix {
let rowStride = MPSMatrixDescriptor.rowBytes(
fromColumns: rawValues.count, dataType: MPSNumberTypeInGPU
)
let descriptor = MPSMatrixDescriptor(
dimensions: 1, columns: rawValues.count, rowBytes: rowStride,
dataType: MPSNumberTypeInGPU
)
guard let inputBuffer = device.makeBuffer(
length: descriptor.matrixBytes, options: MTLResourceOptions.storageModeManaged
) else { fatalError() }
loadMatrix(inputBuffer, rawValues)
return MPSMatrix(buffer: inputBuffer, descriptor: descriptor)
}
func makeMatrix(_ device: MTLDevice, _ cRowsOut: Int, cColumnsOut: Int, rowStride: Int) -> MPSMatrix {
let matrixDescriptor = MPSMatrixDescriptor(
dimensions: cRowsOut, columns: cColumnsOut,
rowBytes: rowStride, dataType: MPSNumberTypeInGPU
)
return MPSMatrix(device: device, descriptor: matrixDescriptor)
}
}
let net = MPSNet()
net.compute()
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 convert a length of time in "Hours:Minutes" to "minutes". Time is given as a String, and I want to return a Double of minutes.
Currently I'm using the following function:
func convertMinHoursToDouble(length: String) -> Double {
var hours = 0.0
var minutes = 0.0
let lengthCleaned = length.stringByReplacingOccurrencesOfString(":", withString: "")
var count = 0
for char in lengthCleaned.characters {
if count == 0 {
hours = Double("\(char)")! * 60
} else if count == 1 {
minutes = Double("\(char)")! * 10
} else if count == 2 {
minutes = Double("\(char)")! + minutes
}
++count
}
return hours+minutes
}
let time = "2:16"
let convertedTime = convertMinHoursToDouble(time)
print(convertedTime) // prints 136.0
This works, however I'm trying to do this in a more functional / Swift way. How can it be done with the reduce function. This is the closest I can get to the solution.
let convertedTime = time.characters.reduce(0) { (dub, char) in dub + Double(String(char))! }
The pure Swift way would be :
let time = "02:16"
let converted = time.characters.split(":")
.flatMap { Int(String($0)) }
.reduce(0) { $0 * 60 + $1 }
print(converted) //"136\n"
Functional solution:
func convertMinHoursToDouble(time: String) -> Int {
let timeComps = (time as NSString).componentsSeparatedByString(":") as [NSString]
return timeComps.reduce(0) { acc, item in
acc * 60 + item.integerValue
}
}
let time = "02:15"
let convertedTime = convertMinHoursToDouble(time)
print(convertedTime)
You split the string into components, and reduce on that array. This works like a charm also for strings like "03:24:34" for which it computes the time in seconds.
You can add additional validation logic if you want to deal with malformed strings: no ":", more than one ":", invalid minutes value (e.g. 78), etc.