Related
I am writing SML program to update records in a list.For example, I have type person_name.
type person_name = {fname:string, lname:string, mname:string}
Then I have person_bio which has person_name embedded in it.
type person_bio = {age:real, gender:string, name:person_name, status:string}
Next I have employee which has person_bio.
type employee = {p:person_bio, payrate:real, whours:real} list;
Now, I have to define function 'updateLastName' by passing the first name.
As of now, created one record 'e1' with below data.
{p={age=40.0,gender="M",name{fname="rob",lname="sen",mname=""},status="M"},
payrate=30.0,whours=10.0}
But I am facing challenge to traverse the list and then updating one field in record.
fun updateLastName(x:string,l:employee)=
if (L=[]) then []
else if (x= #fname(#name(#p hd l)) //cheking name of 1st record in list
//not getting how to update,this kind of line did not work
#fname(#name(#p hd l) = "abc"
else updateLastName(x,tl(l)); // hope this is right
Please suggest.
You have stumbled upon something difficult: Updating a deeply nested record.
For records you have getters, so #fname (#name (#p employee)) gets the field that you're checking against to know that this is the employee whose last name you are going to update. But records don't grant you equivalent setters, so you have to make those. If you're curious, lenses (Haskell) are a general way to solve this, but I don't know of any implementation of lenses for Standard ML.
I'll go ahead and remove the list part in your employee type; you should probably want an employee list if you want multiple employees modelled, rather than to say that an employee is multiple persons.
type person_name = { fname:string, lname:string, mname:string }
type person_bio = { age:real, gender:string, name:person_name, status:string }
type employee = { p:person_bio, payrate:real, whours:real }
val name1 = { fname = "John", lname = "Doe", mname = "W." } : person_name
val bio1 = { age = 42.0, gender = "M", name = name1, status = "?" } : person_bio
val my_employee1 = { p = bio1, payrate = 1000.0, whours = 37.0 } : employee
val name2 = { fname = "Freddy", lname = "Mercury", mname = "X." } : person_name
val bio2 = { age = 45.0, gender = "M", name = name2, status = "?" } : person_bio
val my_employee2 = { p = bio2, payrate = 2000.0, whours = 37.0 } : employee
val my_employees = [ my_employee1, my_employee2 ] : employee list
As for the setters (the ones that you could automatically derive using lenses),
fun setP (p : person_bio, e : employee) =
{ p = p
, payrate = #payrate e
, whours = #whours e } : employee
fun setName (name : person_name, pb : person_bio) =
{ age = #age pb
, gender = #gender pb
, name = name
, status = #status pb } : person_bio
fun setLname (lname, pn : person_name) =
{ fname = #fname pn
, lname = lname
, mname = #mname pn } : person_name
you can compose these, e.g. like:
- setP (setName (setLname ("Johnson", #name (#p my_employee1)), #p my_employee1), my_employee1)
> val it =
{p =
{age = 42.0, gender = "M",
name = {fname = "John", lname = "Johnson", mname = "W."},
status = "?"}, payrate = 1000.0, whours = 37.0} :
{p :
{age : real, gender : string,
name : {fname : string, lname : string, mname : string},
status : string}, payrate : real, whours : real}
Or you can split that line a little apart to make it more readable:
fun updateLname (fname, lname, employees) =
let fun update employee =
if #fname (#name (#p employee)) = fname
then let val new_name = setLname (lname, #name (#p employee))
val new_bio = setName (new_name, #p employee)
val new_employee = setP (new_bio, employee)
in new_employee end
else employee
in List.map update employees
end
Trying this out:
- updateLname ("Freddy", "Johnson", my_employees);
> val it =
[{p = ... {fname = "John", lname = "Doe", mname = "W."}, ... },
{p = ... {fname = "Freddy", lname = "Johnson", mname = "X."}, ... }]
- updateLname ("John", "Johnson", my_employees);
> val it =
[{p = ... {fname = "John", lname = "Johnson", mname = "W."}, ... },
{p = ... {fname = "Freddy", lname = "Mercury", mname = "X."}, ... }]
Depending on your situation, references may be appropriate here.
For any values you may need to change, you can make them a reference, i.e.
type person_name = {fname:string, lname:string ref, mname:string}
type person_bio = {age:real, gender:string, name:person_name, status:string}
fun change_lname(new_lname: string, bio: person_bio) = (#lname (#name bio)) := new_lname
val p1 = ...
print !(#lname (#name p1)) ==> LastName1
change_lname("LastName2", p1)
print !(#lname (#name p1)) ==> LastName2
If you plan on modifying data in a record a lot, it's probably a good idea to make it a reference so that your program is not rewriting memory every time it needs to change one value (though in many situations the compiler/interpreter will be able to optimize this). It also saves you from having to rewrite setter functions if the signature of your record changes. The downside is that you'll be introducing complexity into your program by using references.
For example, in the above code, we're not actually modifying p1's last name, instead p1 and a copy (passed to the function) both point to the same string, and we modify that string in the function. At no point are we actually changing any of the data in either record, we're only changing data that the records point to. It's a subtle difference, and it doesn't really make a difference in this example, but it can lead to strange bugs that are hard to debug.
In Power Query M I am trying to create a recursive function that will turn a mess of multidimensional lists and records into one flat list of records, so that the records can be easily manipulated in PowerBI.
I have worked with recursion in other languages but I am quite new to using M.
The mess of lists and records is similar in structure to this:
Event
Event Details
Payments
Payment Details
There are some minor differences but they shouldn't matter.
I am hoping the output will be similar to this:
{
[event1, eventDetail1, payment1, paymentDetails1],
[event1, eventDetail1, payment1, paymentDetails2],
[event1, eventDetail1, payment1, paymentDetails3],
[event1, eventDetail1, payment2, paymentDetails1],
}
Continuing on for every single item.
This is the recursive function I have currently:
recursiveCollapse = (uncleanedList as list, eventCounter as number, paymentCounter as number, finalList as list) =>
let
eventLength = List.Count(uncleanedList),
firstIf = if eventCounter < eventLength then
let
secondIf = if paymentCounter < List.Count(uncleanedList{eventCounter}[eventPayments]) then
finalList = #recursiveCollapse(uncleanedList, eventCounter, paymentCounter + 1, finalList & {
[
EventName = uncleanedList{eventCounter}[eventDetailName],
EventDescription = uncleanedList{eventCounter}[eventDetailDescription],
EventSaleStatus = uncleanedList{eventCounter}[eventDetailStatus],
EventFirstDate = uncleanedList{eventCounter}[eventDetailFirst],
EventLastDate = uncleanedList{eventCounter}[eventDetailLast],
PaymentID = uncleanedList{eventCounter}[eventPaymentDetails][refs]{paymentCounter}[id],
PaymentName = uncleanedList{eventCounter}[eventPaymentDetails][refs]{paymentCounter}[name],
PaymentCreated = uncleanedList{eventCounter}[eventPayments]{paymentCounter}[paymentDetail][created],
CustomerEmail = uncleanedList{eventCounter}[eventPayments]{paymentCounter}[paymentDetail][customer][emailAddress],
CustomerFirstName = uncleanedList{eventCounter}[eventPayments]{paymentCounter}[paymentDetail][customer][firstName],
CustomerLastName = uncleanedList{eventCounter}[eventPayments]{paymentCounter}[paymentDetail][customer][lastName],
CustomerPhone = uncleanedList{eventCounter}[eventPayments]{paymentCounter}[paymentDetail][customer][mobilePhone],
PaymentStatus = uncleanedList{eventCounter}[eventPayments]{paymentCounter}[paymentDetail][status],
PaymentTotal = uncleanedList{eventCounter}[eventPayments]{paymentCounter}[paymentDetail][totalPrice][value]
]
})
else
finalList = #recursiveCollapse(uncleanedList, eventCounter + 1, 0, finalList)
in
finalList
else
finalList
in
finalList,
dataTable = recursiveCollapse(allEventsLinks, 0, 0, {})
in
dataTable
At this stage "dataTable" is just returned as an empty table.
I believe the problem is due to the "finalList" not being returned correctly through the recursive calls of the function. M does not have a return keyword, so I am lost on what to do from here.
Any help is appreciated.
Thanks
I figured it out.
To anyone else who needs help with this here is my solution:
recursiveCollapse = (uncleanedList as list, eventCounter as number, paymentCounter as
number, finalList as list) =>
let
returnList =
let
eventLength = List.Count(uncleanedList),
eventIf = if eventCounter < eventLength then
let
eventReturn = if paymentCounter + 1 < List.Count(uncleanedList{eventCounter}[eventPayments]) then
let
addRow =
finalList &
{
[
EventName = uncleanedList{eventCounter}[eventDetailName],
EventDescription = uncleanedList{eventCounter}[eventDetailDescription],
EventSaleStatus = uncleanedList{eventCounter}[eventDetailStatus],
EventFirstDate = uncleanedList{eventCounter}[eventDetailFirst],
EventLastDate = uncleanedList{eventCounter}[eventDetailLast],
PaymentID = uncleanedList{eventCounter}[eventPaymentDetails][refs]{paymentCounter + 1}[id],
PaymentName = uncleanedList{eventCounter}[eventPaymentDetails][refs]{paymentCounter + 1}[name],
PaymentCreated = uncleanedList{eventCounter}[eventPayments]{paymentCounter + 1}[paymentDetail][created],
CustomerEmail = uncleanedList{eventCounter}[eventPayments]{paymentCounter + 1}[paymentDetail][customer][emailAddress],
CustomerFirstName = uncleanedList{eventCounter}[eventPayments]{paymentCounter + 1}[paymentDetail][customer][firstName],
CustomerLastName = uncleanedList{eventCounter}[eventPayments]{paymentCounter + 1}[paymentDetail][customer][lastName],
CustomerPhone = uncleanedList{eventCounter}[eventPayments]{paymentCounter + 1}[paymentDetail][customer][mobilePhone],
PaymentStatus = uncleanedList{eventCounter}[eventPayments]{paymentCounter + 1}[paymentDetail][status],
PaymentTotal = uncleanedList{eventCounter}[eventPayments]{paymentCounter + 1}[paymentDetail][totalPrice][value]
]
},
recursion = #recursiveCollapse(uncleanedList, eventCounter, paymentCounter + 1, addRow)
in
recursion
else
let
recursion = #recursiveCollapse(uncleanedList, eventCounter + 1, 0, finalList)
in
recursion
in
eventReturn
else
finalList
in
eventIf
in
returnList,
dataTable = Table.FromList(recursiveCollapse(allEventsLinks, 0, 0, {}), Record.FieldValues, {
"EventName",
"EventDescription",
"EventSaleStatus",
"EventFirstDate",
"EventLastDate",
"PaymentID",
"PaymentName",
"PaymentCreated",
"CustomerEmail",
"CustomerFirstName",
"CustomerLastName",
"CustomerPhone",
"PaymentStatus",
"PaymentTotal"
})
in
dataTable
I was wondering about programmatically adding a NSTableView inside a NSStackView using Swift 3/MacOS Sierra.
The idea would be to have say 2 NSTextFields aligned via the centerY axis in the .leading gravity space, then a tableview in the .center gravity space, then 2 more NSTextFields aligned via the centerY axis in the .trailing gravity space. The stack view would span the width of the NSView -- like a header.
Is this a good idea or should I avoid doing this? It has been very difficult to get it to look correct -- the table always has too large of a width despite adding constraints to try to pin it to a fixed width.
Any insight would be appreciated. I'm new to programming MacOS.
Thanks,
Here is the output in Interface Builder:
output of the headerview
Here is the code of the NSView I'm using:
The view controller is elsewhere but I'm not really having problems with the view controller -- it's displaying the data in the table correctly. It's just the sizing/positioning of the tableview (which I'm trying to do in the NSView via the NSStackView) is always wrong. It should have a width of 650 but instead has a width of 907 and I get the same error all the time in the debug console:
2017-09-12 17:43:36.041062-0500 RaceProgram[795:36958] [Layout] Detected missing constraints for < RacingProgram.RaceImportViewHeader: 0x6000001ccd50 >. It cannot be placed because there are not enough constraints to fully define the size and origin. Add the missing constraints, or set translatesAutoresizingMaskIntoConstraints=YES and constraints will be generated for you. If this view is laid out manually on macOS 10.12 and later, you may choose to not call [super layout] from your override. Set a breakpoint on DETECTED_MISSING_CONSTRAINTS to debug. This error will only be logged once.
import Cocoa
#IBDesignable
class RaceImportViewHeader: NSView {
// MARK: Properties
private var raceQualificationsTableView:NSTableView
private var raceImportHeaderStackView:NSStackView
private var raceNumberTitle: NSTextField
private var raceNumberValue: NSTextField
public var raceQualificationsTableRowHeight: CGFloat
#IBInspectable var borderColor:NSColor = .black
#IBInspectable var backgroundColor:NSColor = .lightGray
enum InitMethod {
case Coder(NSCoder)
case Frame(CGRect)
}
override convenience init(frame: CGRect) {
self.init(.Frame(frame))!
}
required convenience init?(coder: NSCoder) {
self.init(.Coder(coder))
}
private init?(_ initMethod: InitMethod) {
// Group together the initializers for this view class
raceQualificationsTableView = NSTableView()
raceImportHeaderStackView = NSStackView()
raceNumberTitle = NSTextField()
raceNumberValue = NSTextField()
raceQualificationsTableRowHeight = 17.0 // Initialize the row height for raceQualifications
switch initMethod {
case let .Coder(coder): super.init(coder: coder)
case let .Frame(frame): super.init(frame: frame)
}
self.translatesAutoresizingMaskIntoConstraints = false
drawUI()
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let viewSize: NSRect = self.frame
let newRect = NSRect(x: 0, y: 0, width: viewSize.width, height: viewSize.height)
// Outline the Header --> Only for layout debug purposes
let path = NSBezierPath(rect: newRect)
backgroundColor.setFill()
path.fill()
borderColor.setStroke() // Set the stroke color
path.stroke() // Fill the stroke or border of the rectangle
}
// MARK: UI Construction
func drawUI() {
let viewFrame = self.frame // with respect to the super class
let viewBounds = self.bounds // with respect to the view
// MARK: Race Number Setup
func addRaceNumberTitle(startingPositionX: CGFloat) {
// This configures label for race Number
let width:CGFloat = 60.0 //Arbitrary at the moment
let height:CGFloat = 40.0
let leftPadding:CGFloat = 2.5 // The super view (frame)is the NSView in this case
let topPadding:CGFloat = (viewBounds.height - height)/2
let raceNumberTitleNSRect = NSRect(x: leftPadding + startingPositionX, y: viewBounds.height - height - topPadding, width: width, height: height)
//Swift.print("The raceNumberTitleNSRect title NSRect is \(raceNumberTitleNSRect)")
raceNumberTitle = NSTextField(frame: raceNumberTitleNSRect)
raceNumberTitle.stringValue = "Race\nNumber"
raceNumberTitle.maximumNumberOfLines = 2
raceNumberTitle.isEditable = false
raceNumberTitle.isBordered = false
raceNumberTitle.alignment = .center
raceNumberTitle.backgroundColor = .clear
raceNumberTitle.sizeToFit()
let updatedHeight = raceNumberTitle.frame.height
let newUpdatedPadding = (viewBounds.height - updatedHeight) / 2
let oldOriginX = raceNumberTitle.frame.origin.x
let newOriginY = viewBounds.height - updatedHeight - newUpdatedPadding
let newOrigin = NSPoint(x: oldOriginX, y: newOriginY)
raceNumberTitle.setFrameOrigin(newOrigin)
//addSubview(raceNumberTitle) // Add to view
raceImportHeaderStackView.addView(raceNumberTitle, in: .leading)
}
func addRaceNumberValue(startingPositionX: CGFloat) {
// This configures value label for race number
let width:CGFloat = 20.0 //Arbitrary at the moment
let height:CGFloat = 40.0
let leftPadding:CGFloat = 5.0 // The super view (frame)is the NSView in this case
let topPadding:CGFloat = (viewBounds.height - height)/2
let raceNumberInRect = NSRect(x: startingPositionX + leftPadding, y: viewBounds.height - height - topPadding, width: width, height: height)
Swift.print("The raceNumberInRect title NSRect is \(raceNumberInRect)")
raceNumberValue = NSTextField(frame: raceNumberInRect)
raceNumberValue.identifier = "raceNumber"
raceNumberValue.placeholderString = "1"
raceNumberValue.font = NSFont(name: "Impact", size: 20.0)
raceNumberValue.maximumNumberOfLines = 1
raceNumberValue.isEditable = false
raceNumberValue.isBordered = true
raceNumberValue.alignment = .center
raceNumberValue.backgroundColor = .clear
raceNumberValue.sizeToFit()
let updatedHeight = raceNumberValue.frame.height
let oldOriginX = raceNumberValue.frame.origin.x
let newUpdatedPadding = (viewBounds.height - updatedHeight) / 2
let newOriginY = viewBounds.height - updatedHeight - newUpdatedPadding
let newOrigin = NSPoint(x: oldOriginX, y: newOriginY)
raceNumberValue.setFrameOrigin(newOrigin)
//addSubview(raceNumberValue) // Add to view
raceImportHeaderStackView.addView(raceNumberValue, in: .leading)
}
// MARK: Race Qualifications Table Setup
func addRaceQualificationsTable(startingPositionX: CGFloat) {
// Padding variables
let leftPadding:CGFloat = 5.0
let topPadding:CGFloat = 5.0
// Table Properties
let width:CGFloat = 650.0
let height:CGFloat = 40
let tableRect = CGRect(x: startingPositionX + leftPadding, y: viewBounds.height - height - topPadding, width: width, height: height)
//let insetForTableView:CGFloat = 1.0
//let scrollRect = CGRect(x: tableRect.origin.x-insetForTableView, y: tableRect.origin.y-insetForTableView, width: tableRect.width+2*insetForTableView, height: tableRect.height+2*insetForTableView)
let tableNSSize = NSSize(width: tableRect.width, height: tableRect.height)
let scrollNSRect = NSScrollView.frameSize(forContentSize: tableNSSize, horizontalScrollerClass: nil, verticalScrollerClass: nil, borderType: .bezelBorder, controlSize: .regular, scrollerStyle: .legacy)
Swift.print("tableRect \(tableRect)")
Swift.print("scrollNSRect \(scrollNSRect)")
//Swift.print("scrollRect \(scrollRect)")
let scrollViewOrigin:CGPoint = tableRect.origin
let scrollViewNSSize:CGSize = scrollNSRect
let scrollRect = NSRect(origin: scrollViewOrigin, size: scrollViewNSSize)
Swift.print("scrollRect \(scrollRect)")
let tableScrollView = NSScrollView(frame: scrollRect)
raceQualificationsTableView = NSTableView(frame: tableRect)
raceQualificationsTableView.identifier = "raceQualificationsTable" // Setup identifier
raceQualificationsTableView.rowHeight = 20.0
Swift.print("instrinic size \(raceQualificationsTableView.intrinsicContentSize)")
//Swift.print("tableScrollView contentsize \(tableScrollView.contentSize)")
tableScrollView.documentView = raceQualificationsTableView
tableScrollView.autoresizingMask = .viewNotSizable
Swift.print("tableScroll content size \(tableScrollView.contentSize)")
//self.addSubview(tableScrollView)
raceImportHeaderStackView.addView(tableScrollView, in: .center)
}
func configureRaceQualificationsTable(showRaceNumberCol: Bool, showRaceCodeCol: Bool) {
let headerAlignment = NSTextAlignment.center // Easy way to change justification of headers
// MARK: Race Number Column Options
let raceNumberColumn = NSTableColumn(identifier: "raceNumberCol")
raceNumberColumn.title = "Race"
raceNumberColumn.minWidth = 40.0
raceNumberColumn.width = 40.0
raceNumberColumn.headerToolTip = "Race Number from the Imported Card"
raceNumberColumn.headerCell.alignment = headerAlignment
// Note: Word Race is always going to be wider than the race number value
// So size to Fit is appropriate here.
raceNumberColumn.sizeToFit()
if showRaceNumberCol {
// Option of not adding this to the table
raceQualificationsTableView.addTableColumn(raceNumberColumn)
}
// MARK: Driver Column Options
let breedColumn = NSTableColumn(identifier: "driverCol")
driverColumn.title = "Driver"
driverColumn.minWidth = 10
driverColumn.headerToolTip = "Driver information"
driverColumn.headerCell.alignment = headerAlignment
driverColumn.sizeToFit()
raceQualificationsTableView.addTableColumn(driverColumn)
// MARK: Race Code Column Options
let raceTypeCodeColumn = NSTableColumn(identifier: "raceTypeCodeCol")
raceTypeCodeColumn.title = "Race Code"
raceTypeCodeColumn.minWidth = 40
raceTypeCodeColumn.headerToolTip = "Race Classification Code"
raceTypeCodeColumn.headerCell.alignment = headerAlignment
raceTypeCodeColumn.sizeToFit()
if showRaceCodeCol {
// Option of not adding to the table
raceQualificationsTableView.addTableColumn(raceTypeCodeColumn)
}
// MARK: Race Type Code Description Options
let raceTypeCodeDescColumn = NSTableColumn(identifier: "raceTypeCodeDescCol")
raceTypeCodeDescColumn.title = "Race Desc"
raceTypeCodeDescColumn.minWidth = 50
raceTypeCodeDescColumn.width = 100
raceTypeCodeDescColumn.headerToolTip = "Race Classification Full Description"
raceTypeCodeDescColumn.headerCell.alignment = headerAlignment
raceQualificationsTableView.addTableColumn(raceTypeCodeDescColumn)
// MARK: Race Restriction Column Options
let raceRestrictionColumn = NSTableColumn(identifier: "raceRestrictionCol")
raceRestrictionColumn.title = "Restrictions"
raceRestrictionColumn.minWidth = 50
raceRestrictionColumn.width = 80
raceRestrictionColumn.headerToolTip = "Race Restrictions"
raceRestrictionColumn.headerCell.alignment = headerAlignment
raceQualificationsTableView.addTableColumn(raceRestrictionColumn)
// MARK: Sex Restriction Column Options
let sexRestrictionColumn = NSTableColumn(identifier: "sexRestrictionCol")
sexRestrictionColumn.title = "Sex"
sexRestrictionColumn.minWidth = 100
sexRestrictionColumn.width = 100
sexRestrictionColumn.headerToolTip = "Sex Restrictions"
sexRestrictionColumn.headerCell.alignment = headerAlignment
raceQualificationsTableView.addTableColumn(sexRestrictionColumn)
// MARK: Age Restriction Column Options
let ageRestrictionColumn = NSTableColumn(identifier: "ageRestrictionCol")
ageRestrictionColumn.title = "Age"
ageRestrictionColumn.minWidth = 100
ageRestrictionColumn.width = 100
ageRestrictionColumn.headerToolTip = "Age Restrictions"
ageRestrictionColumn.headerCell.alignment = headerAlignment
raceQualificationsTableView.addTableColumn(ageRestrictionColumn)
// MARK: Division Column Options
let divisionColumn = NSTableColumn(identifier: "divisionCol")
divisionColumn.title = "Division"
divisionColumn.minWidth = 50
let minDivisionColumnWidth = raceQualificationsTableView.frame.width - raceNumberColumn.width - driverColumn.width - raceTypeCodeColumn.width - raceTypeCodeDescColumn.width - raceRestrictionColumn.width - sexRestrictionColumn.width - ageRestrictionColumn.width
// Calculate the available room for the division column
if (showRaceCodeCol && showRaceNumberCol) {
// This is the minimum case
// No idea why we need the 25.0 manual adjustment
divisionColumn.width = minDivisionColumnWidth - 25.0
} else if (showRaceCodeCol && !showRaceNumberCol) {
// Add back race type code
// No idea why we need to manually adjust 53.5
divisionColumn.width = minDivisionColumnWidth + raceTypeCodeColumn.width - 53.5
} else if (!showRaceCodeCol && showRaceNumberCol) {
// Add back race number col
divisionColumn.width = minDivisionColumnWidth + raceNumberColumn.width
} else {
// Else it's the maximum space
// This code was making the frame too large -- it was increasing the
// the frame size of the column to 670.0 I put a manual reduction of
// 20 to keep the frame size the same. Not sure where this 20 is coming from.
divisionColumn.width = minDivisionColumnWidth + raceNumberColumn.width + raceTypeCodeColumn.width - 20.0
}
//Swift.print("The division column width is \(divisionColumn.width)")
divisionColumn.headerToolTip = "Division -- Unknown what this means"
divisionColumn.headerCell.alignment = headerAlignment
raceQualificationsTableView.addTableColumn(divisionColumn)
//Swift.print("raceQualificationsTableView.frame.width is \( raceQualificationsTableView.frame.width)")
}
// MARK: Race Distance Surface Course Setup
func addRaceDistanceSurfaceCourseTable(startingPositionX: CGFloat) {
// Table Properties
let width:CGFloat = 250.0
let height:CGFloat = 40.0
// Padding variables
let leftPadding:CGFloat = 5.0
let topPosition:CGFloat = (viewBounds.height - ((viewBounds.height - height)/2) - height)
let tableRect = CGRect(x: leftPadding + startingPositionX, y: topPosition, width: width, height: height)
let tableScrollView = NSScrollView(frame: tableRect)
raceDistanceSurfaceCourseTableView = NSTableView(frame: tableRect)
raceDistanceSurfaceCourseTableView.identifier = "raceDistanceSurfaceCourseTable" // Setup identifier
//raceDistanceSurfaceCourseTableView.rowHeight = 20.0
raceDistanceSurfaceCourseTableView.intercellSpacing = NSSize(width: 1.0, height: 1.0)
raceDistanceSurfaceCourseTableView.headerView = ImportRaceTableHeaders()
tableScrollView.documentView = raceDistanceSurfaceCourseTableView
//tableScrollView.hasVerticalScroller = false
//tableScrollView.verticalScroller = nil // Turn off vertical scrolling
//tableScrollView.verticalScrollElasticity = .none
//raceDistanceSurfaceCourseTableView = NSTableViewHeader
//self.addSubview(tableScrollView)
raceImportHeaderStackView.addView(raceDistanceSurfaceCourseTableView, in: .center)
}
// MARK: Construct the fields:
//configureHeaderView()
configureStackView()
addRaceNumberTitle(startingPositionX: 0.0) // Add the race number title
addRaceNumberValue(startingPositionX: raceNumberTitle.frame.origin.x + raceNumberTitle.frame.width) //Add the Race Number value text field
addRaceQualificationsTable(startingPositionX: raceNumberValue.frame.origin.x + raceNumberValue.frame.width)
configureRaceQualificationsTable(showRaceNumberCol: false, showRaceCodeCol: false)
}
// MARK: TableView Functions
func reloadTableViewData(identifier: String) {
Swift.print("Manual reload of data for identifier \(identifier)")
switch identifier {
case "raceQualificationsTable":
raceQualificationsTableView.reloadData()
case "raceDistanceSurfaceCourseTable":
raceDistanceSurfaceCourseTableView.reloadData()
default:
break
}
}
// MARK: Delegate/DataSources Outlets for TableViews
// Race Qualification Table (the header table)
#IBOutlet weak var raceQualificationsDelegate: NSTableViewDelegate? {
get {
return raceQualificationsTableView.delegate
}
set {
raceQualificationsTableView.delegate = newValue
}
}
#IBOutlet weak var raceQualificationsDataSource: NSTableViewDataSource? {
get {
return raceQualificationsTableView.dataSource
}
set {
raceQualificationsTableView.dataSource = newValue
}
}
// Race Distance Surface Course
#IBOutlet weak var raceDistanceSurfaceCourseDelegate: NSTableViewDelegate? {
get {
return raceDistanceSurfaceCourseTableView.delegate
}
set {
raceDistanceSurfaceCourseTableView.delegate = newValue
}
}
#IBOutlet weak var raceDistanceSurfaceCourseDataSource: NSTableViewDataSource? {
get {
return raceDistanceSurfaceCourseTableView.dataSource
}
set {
raceDistanceSurfaceCourseTableView.dataSource = newValue
}
}
// MARK: Label Outlets
#IBOutlet var raceNumber:String? {
get {
return raceNumberValue.stringValue
}
set {
raceNumberValue.stringValue = newValue!
}
}
}
class Flight{
var name:String?
var vocabulary:Vocabulary?
}
class Vocabulary{
var seatMapPlan:[Plan] = []
var foodPlan:[Plan] = []
}
class Plan{
var planName:String?
var planId:String?
}
var flightList:[Flight] = []
var plan1 = Plan()
plan1.planId = "planId1"
plan1.planName = "Planname1"
var plan2 = Plan()
plan2.planId = "planId2"
plan2.planName = "Planname2"
var plan3 = Plan()
plan3.planId = "planId3"
plan3.planName = "Planname3"
var plan4 = Plan()
plan4.planId = "planId4"
plan4.planName = "Planname4"
var plan5 = Plan()
plan5.planId = "planId5"
plan5.planName = "Planname5"
var plan6 = Plan()
plan6.planId = "planId6"
plan6.planName = "Planname6"
var flight1 = Flight()
flight1.name = "Flight1"
flight1.vocabulary = Vocabulary()
flight1.vocabulary?.seatMapPlan = [plan1, plan2]
flight1.vocabulary?.foodPlan = [plan3, plan4, plan5]
var flight2 = Flight()
flight2.name = "Flight2"
flight2.vocabulary = Vocabulary()
flight2.vocabulary?.seatMapPlan = [plan2, plan3]
flight2.vocabulary?.foodPlan = [plan3, plan4, plan5]
flightList=[flight1, flight2]
Problem 1:
I want to use flatmap,filter,custom unique func or Sets.formUnion to achieve a union of seatMapPlans. For this particular example it is
seatMapUnion = [plan1,plan2,plan3]
Because of nesting with the help of answered questions I am unable to achieve this.
Please give me a combination of filter,flatMap and map for resolving this particular problem.
Problem 2:
I have vice-versa scenarios too were i have to sort this array flightList on basis of plan(plan1 or multiple) selected. I want to sort this on basis of filter and map, but the nesting is making it difficult to achieve.
e.g. 1:
if the search parameter is plan1 for seatMapPlan. Then the result is flight1.
e.g. 2:
And if the search parameter is plan2 for seatMapPlan. Then the result is flight1,flight2.
For the first problem I would use sets. So first make Plan implement Hashable :
class Plan : Hashable {
var planName:String?
var planId:String?
public var hashValue: Int { return planName?.hashValue ?? 0 }
public static func ==(lhs: Plan, rhs: Plan) -> Bool { return lhs.planId == rhs.planId }
}
Then it's straightforward :
let set1 = Set<Plan>(flight1.vocabulary!.seatMapPlan)
let set2 = Set<Plan>(flight2.vocabulary!.seatMapPlan)
let union = set1.union(set2)
print(union.map { $0.planName! } )
It'll print:
["Planname2", "Planname1", "Planname3"]
Not sure I understand your second problem.
Suppose I have two arrays:
let myKeys = ["one", "two", "three"]
let myValues = ["1", "2", "3"]
and empty dictionary:
var myDictionary = Dictionary<String, String>()
What is the best way to assign myKeys and myValues as keys and values, respectively, of myDictionary?
With my knowledge, this is the way to assign values/keys for your dictionary:
let count = myKeys.count
let count2 = myValues.count
if (count == count2) {
for index in 0..count {
myDictionary.updateValue(myValues[index], forKey:myKeys[index])
}
}
Edit: Swift 2
let count = myKeys.count
let count2 = myValues.count
if (count == count2) {
for index in 0 ..< count {
myDictionary.updateValue(myValues[index], forKey:myKeys[index])
}
}