I am trying to add up the values found within some custom objects in Swift. I have the follow code working in a Playground:
//Custom Object
class EntryPack: NSObject{
var date: String!
var duration: Double!
var customValues:[CustomValuePack] = [] //Nested array of objects declared below
}
//Another object to nest in the above
class CustomValuePack: NSObject{
var name:String!
var value: Double!
}
//Instantiate nested objects
let cv1 = CustomValuePack()
cv1.name = "Day"
cv1.value = 1.2
let cv2 = CustomValuePack()
cv2.name = "Day"
cv2.value = 1.3
let cv3 = CustomValuePack()
cv2.name = "Night"
cv2.value = 2.2
//Instantiate parent objects
let entry1 = EntryPack()
entry1.date = "bingo"
entry1.duration = 1.1
entry1.customValues = [cv1, cv2]
let entry2 = EntryPack()
entry2.date = "bingo"
entry2.duration = 2.2
let entry3 = EntryPack()
entry3.date = "dang"
entry3.duration = 3.0
//Throw it all together into an array
var package = [entry1, entry2, entry3]
//Predicate for some filtering
let predicate = NSPredicate(format:"date = %#", "bingo")
let results = (package as NSArray).filteredArrayUsingPredicate(predicate) as! [EntryPack]
//Sum all the parent object durations
let sum = results.reduce(0.0) { $0 + $1.duration }
print(sum) // = 3.3
But now I want to add up the value for each CustomValuePack where the name is "Day".
I can't figure out how to do a similar reduce on a nested array of objects. I've tried something like this, but it yields a syntax error:
let sum2 = results.reduce(0.0) { $0.customValues + $1.customValues.value } //Error
How do I sum the value in a nested array of objects? I eventually want to apply an NSPredicate to filter by name = Day as well, but I'm not that far yet.
Given entries defined as follow
let entries = [entry1, entry2, entry3]
you can extract the CustomValues with name == "Day" and sum the value field with this code
let sum = entries
.flatMap { $0.customValues }
.filter { $0.name == "Day" }
.map { $0.value }
.reduce(0, combine: +)
Improvements
You are using Swift but for some reason there still is a lot of Objective-C into your code.
This is how i would refactor your code
struct EntryPack {
let date: String
let duration: Double
let customValues:[CustomValuePack]
}
struct CustomValuePack {
let name:String
let value: Double
}
let cv1 = CustomValuePack(name: "Day", value: 1.2)
let cv2 = CustomValuePack(name: "Day", value: 1.3)
let cv3 = CustomValuePack(name: "Night", value: 2.2)
let entry1 = EntryPack(date: "bingo", duration: 1.1, customValues: [cv1, cv2])
let entry2 = EntryPack(date: "bingo", duration: 2.2, customValues: [])
let entry3 = EntryPack(date: "dang", duration: 3.0, customValues: [])
Please note that now EntryPack and CustomValuePack are structs which are value types.
Related
I want to sort my Realm object, using one of it properties. It has WBS like format (1.1.3 ,1.1.11, etc) using String as it type.
I'm using RealmSwift 3.11.2 and I've already tried using sorted(by:) and it works! But the 1.1.10 and 1.1.11 will be sorted ahead of 1.1.2
This is the code that I'm using
tasks = tasks.sorted(byKeyPath: "wbs", ascending: true)
I expect the output will be ordered correctly, like [1.1.2, 1.1.10, 1.1.11].
Any help is appreciated, I'm drawing a blank here, is there any way I can do it in Realm ?
You may want to take a minute and do an Internet search on sorting strings.
The issue is that if you have a list of strings like this 1, 2, 3, 10 and they are sorted, they will sort to 1, 10, 2, 3.
This is because how strings are sorted; for this example they are they are generally sorted by the first letter/object in the string (1's) and then by the second letter (0) and so on, so then the strings that start with 1 get grouped up. Then 2's etc.
So you need to either pad them so the formats are the same like
01.01.02, 01.01.10, 01.01.11 etc
which will sort correctly or store the three sections in separate properties as numbers (int's)
class TriadClass {
var first = 0
var second = 0
var third = 0
init(x: Int, y: Int, z: Int) {
self.first = x
self.second = y
self.third = z
}
func getPaddedString() -> String {
let formatter = NumberFormatter()
formatter.format = "00"
let firstNum = NSNumber(value: self.first)
let secondNum = NSNumber(value: self.second)
let thirdNum = NSNumber(value: self.third)
let firstString = formatter.string(from: firstNum)
let secondString = formatter.string(from: secondNum)
let thirdString = formatter.string(from: thirdNum)
let finalString = "\(firstString!).\(secondString!).\(thirdString!)"
return finalString
}
func getPureString() -> String {
let formatter = NumberFormatter()
formatter.format = "0"
let firstNum = NSNumber(value: self.first)
let secondNum = NSNumber(value: self.second)
let thirdNum = NSNumber(value: self.third)
let firstString = formatter.string(from: firstNum)
let secondString = formatter.string(from: secondNum)
let thirdString = formatter.string(from: thirdNum)
let finalString = "\(firstString!).\(secondString!).\(thirdString!)"
return finalString
}
}
Then sort by first, second and third. To get the number for display in the UI I included to functions to format them. Note both functions could be greatly improved and shortened by I left them verbose so you could step through.
The usage would be
let myNum = TriadClass(x: 1, y: 1, z: 10)
let paddedString = myNum.getPaddedString()
print(paddedString)
let nonPaddedString = myNum.getPureString()
print(nonPaddedString)
and an output of
01.01.10
1.1.10
There are a number of other solutions as well but these two are a place to start.
I m new to programming I just playing around in playground, I am trying to loop in a dictionary to do some calculation and add them into new dictionary however, I can only add one dictionary value back. I am not sure why. Can anymore tell me and point out the point?
var name:String?
var popDict: [String: Array<Double>]?
var FinalDict: [String: Array<Double>]?
var mean: Double?
var elementSideArr: Array<Double>?
var nameArray = ["Olivia": [1,2,2,2,1,2,1,0.0001,0,1,2], "Amber": [52,52,65,66,57,63,62,0.0001,0,0,0]]
var doubleArr = [Double]()
for (key, value) in nameArray{
let thisValue = value
let arrayS = thisValue.prefix(7)
let slice = arrayS[0...6]
let popSideArr = Array(slice)
let average: Double = (popSideArr as NSArray).value(forKeyPath: "#avg.self") as! Double
mean = average
let thatValue = value
let arraySS = thatValue.suffix(3)
let sslice = arraySS[8...10]
elementSideArr = Array(sslice)
elementSideArr?.insert(average, at: 0)
let dict = ["\(key)": elementSideArr!]
for (key,value) in dict{
popDict = dict
}
}
print(popDict!)
["Olivia": [1.5714285714285714, 0.0, 1.0, 2.0]] // It is the only output but it should be 2 items.
Problem is in your inner for loop you are initializing whole new dictionary to popDict also you don't need for loop if you are having single key and value pair with your dictionary.
Replace line of codes:
let dict = ["\(key)": elementSideArr!]
for (key,value) in dict{
popDict = dict
}
With:
popDict["\(key)"] = elementSideArr!
Note: No need to cast swift array to NSArray to calculate average you can simply use reduce to get total and then divide the total with array count.
let array = popSideArr as? [Int] ?? []
let res = Double(array.reduce(0, +)) / Double(popSideArr.count)
I'm receiving this JSON:
JSON: {
"status_code" : 200,
"status" : "ok",
"data" : [
{
"zona" : "Narvarte",
"hora" : "",
"id_zona" : 1423,
"proxdia" : "Lunes 20 de Febrero, 2017",
"coor" : "(19.452187074041884, -99.1457748413086),(19.443769985032485, -99.14852142333984),(19.443446242121073, -99.13787841796875),(19.450244707639662, -99.13822174072266)",
"dias" : "Lunes"
}, ...]
Which I'm storing in this struct:
struct RutaItem {
var idZona: Int
var dias: String
var proxDia: String
var hora: String
var coor: String
var zona: String
}
then I created an array of [RutaItem] where I'm storing the structs
var rutaItemArray = [RutaItem]()
Once the data has been stored the structs inside rutaItemArray look like this:
[pixan.RutaItem(idZona: 1423, dias: "Lunes", proxDia: "Lunes 20 de Febrero, 2017", hora: "", coor: "(19.452187074041884, -99.1457748413086),(19.443769985032485, -99.14852142333984),(19.443446242121073, -99.13787841796875),(19.450244707639662, -99.13822174072266)", zona: "Narvarte")...]
What I need to do now is to use the String inside each index of rutaItemArray.coor to generate an MKPolygonObject, so first I would need to convert the long String into 4 CLLocationCoordinate2D objects and put those 4 coordinate objects inside an array for each item, then use the array indexes to generate the polygon.for the different areas.
Can somebody help me with this problem?
You can use regular expression pattern matching.
Explanations inline:
let coordString = "(19.452187074041884, -99.1457748413086), (19.443769985032485, -99.14852142333984),(19.443446242121073, -99.13787841796875),(19.450244707639662, -99.13822174072266)"
// Regular expression pattern for "( ... , ... )"
let pattern = "\\((.+?),(.+?)\\)"
let regex = try! NSRegularExpression(pattern: pattern)
// We need an NSString, compare http://stackoverflow.com/a/27880748/1187415
let nsString = coordString as NSString
// Enumerate all matches and create an array:
let coords = regex.matches(in: coordString, range: NSRange(location: 0, length: nsString.length))
.flatMap { match -> CLLocationCoordinate2D? in
// This closure is called for each match.
// Extract x and y coordinate from match, remove leading and trailing whitespace:
let xString = nsString.substring(with: match.rangeAt(1)).trimmingCharacters(in: .whitespaces)
let yString = nsString.substring(with: match.rangeAt(2)).trimmingCharacters(in: .whitespaces)
// Convert to floating point numbers, skip invalid entries:
guard let x = Double(xString), let y = Double(yString) else { return nil }
// Return CLLocationCoordinate2D:
return CLLocationCoordinate2D(latitude: x, longitude: y)
}
Here's what I came up with. You can adapt it to fit your structure.
import Foundation
let input = "(19.452187074041884, -99.1457748413086),(19.443769985032485, -99.14852142333984),(19.443446242121073, -99.13787841796875),(19.450244707639662, -99.13822174072266)"
// Remove leading `(` and trailing `)`
let trimmedInput = String(input.characters.dropLast().dropFirst())
let coordStrings = trimmedInput.components(separatedBy: "),(")
let coords: [CLLocationCoordinate2D] = coordStrings.map{ coordsString in
let coords = coordsString.components(separatedBy: ", ")
precondition(coords.count == 2, "There should be exactly 2 coords.")
guard let lat = Double(coords[0]),
let long = Double(coords[1]) else {
fatalError("One of the coords isn't a valid Double: \(coords)")
}
return CLLocationCoordinate2D(latitude: lat, longitude: long)
}
print(coords)
I have a core data data model with two entities, Document and Field:
Document:
fields [to many relationship]
Field:
name
fieldText
fieldDate
fieldNumber
document [to one relationship]
I am trying to set up an NSPredicateEditor to allow someone to filter documents based on the content of certain fields. I manually build the predicate editor like so:
func updatePredicateEditor() {
print("updatePredicateEditor")
let sortDescriptor = NSSortDescriptor(key: "orderIndex", ascending: true)
fieldsArrayController.sortDescriptors = [sortDescriptor]
let fields = fieldsArrayController.arrangedObjects as! [Field]
var keyPathsStringArray = [NSExpression]()
var stringFieldNames = [String]()
var keyPathsDateArray = [NSExpression]()
var dateFieldNames = [String]()
var keyPathsNumberArray = [NSExpression]()
var numberFieldNames = [String]()
for i in 0..<fields.count {
let currentField = fields[i]
switch currentField.type! {
case "Text":
keyPathsStringArray.append(NSExpression(forKeyPath: "fieldText"))
stringFieldNames.append(currentField.name!)
case "Date":
keyPathsDateArray.append(NSExpression(forKeyPath: "fieldDate"))
dateFieldNames.append(currentField.name!)
case "Number":
keyPathsNumberArray.append(NSExpression(forKeyPath: "fieldNumber"))
numberFieldNames.append(currentField.name!)
default:
print("error on field type")
}
}
let stringOperators = [NSNumber(unsignedInteger: NSPredicateOperatorType.ContainsPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.EqualToPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.NotEqualToPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.BeginsWithPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.EndsWithPredicateOperatorType.rawValue)]
let numberOperators = [NSNumber(unsignedInteger: NSPredicateOperatorType.EqualToPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.NotEqualToPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.LessThanPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.LessThanOrEqualToPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.GreaterThanPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.GreaterThanOrEqualToPredicateOperatorType.rawValue)]
let dateOperators = [NSNumber(unsignedInteger: NSPredicateOperatorType.EqualToPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.NotEqualToPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.LessThanPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.LessThanOrEqualToPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.GreaterThanPredicateOperatorType.rawValue),
NSNumber(unsignedInteger: NSPredicateOperatorType.GreaterThanOrEqualToPredicateOperatorType.rawValue)]
var rowTemplatesTemp = [NSPredicateEditorRowTemplate]() // this is a temp array to hold the different popupbuttons
// add a template for Strings
let leftExpressionStringButton : NSPopUpButton
if keyPathsStringArray.count == 0 {
print("There aren't any text fields in NSPredicateEditor")
}
else {
let stringTemplate = NSPredicateEditorRowTemplate(leftExpressions: keyPathsStringArray,
rightExpressionAttributeType: NSAttributeType.StringAttributeType,
modifier: NSComparisonPredicateModifier.DirectPredicateModifier,
operators: stringOperators,
options: (Int(NSComparisonPredicateOptions.CaseInsensitivePredicateOption.rawValue) |
Int(NSComparisonPredicateOptions.DiacriticInsensitivePredicateOption.rawValue)))
leftExpressionStringButton = stringTemplate.templateViews[0] as! NSPopUpButton
let stringButtonArray = leftExpressionStringButton.itemTitles
for i in 0..<stringButtonArray.count {
(leftExpressionStringButton.itemAtIndex(i)! as NSMenuItem).title = stringFieldNames[i] // set button menu names
}
rowTemplatesTemp.append(stringTemplate)
}
// add another template for Numbers...
let leftExpressionNumberButton : NSPopUpButton
if keyPathsNumberArray.count == 0 {
print("There aren't any number fields in NSPredicateEditor")
}
else {
let numberTemplate = NSPredicateEditorRowTemplate(leftExpressions: keyPathsNumberArray,
rightExpressionAttributeType: NSAttributeType.Integer32AttributeType,
modifier: NSComparisonPredicateModifier.DirectPredicateModifier,
operators: numberOperators,
options: 0)
leftExpressionNumberButton = numberTemplate.templateViews[0] as! NSPopUpButton
let numberButtonArray = leftExpressionNumberButton.itemTitles
for i in 0..<numberButtonArray.count {
(leftExpressionNumberButton.itemAtIndex(i)! as NSMenuItem).title = numberFieldNames[i] // set button menu names
}
rowTemplatesTemp.append(numberTemplate)
}
// add another template for Dates...
let leftExpressionDateButton : NSPopUpButton
if keyPathsDateArray.count == 0 {
print("There aren't any date fields in NSPredicateEditor")
}
else {
let dateTemplate = NSPredicateEditorRowTemplate(leftExpressions: keyPathsDateArray,
rightExpressionAttributeType: NSAttributeType.DateAttributeType,
modifier: NSComparisonPredicateModifier.DirectPredicateModifier,
operators: dateOperators,
options: 0)
leftExpressionDateButton = dateTemplate.templateViews[0] as! NSPopUpButton
let dateButtonArray = leftExpressionDateButton.itemTitles
for i in 0..<dateButtonArray.count {
(leftExpressionDateButton.itemAtIndex(i)! as NSMenuItem).title = dateFieldNames[i] // set button menu names
}
rowTemplatesTemp.append(dateTemplate)
}
// create the any, all or none thing...
let compoundTypes = [NSNumber.init(unsignedInteger: NSCompoundPredicateType.OrPredicateType.rawValue),
NSNumber.init(unsignedInteger: NSCompoundPredicateType.AndPredicateType.rawValue),
NSNumber.init(unsignedInteger: NSCompoundPredicateType.NotPredicateType.rawValue)]
// add the compoundtypes
let compoundTemplate = NSPredicateEditorRowTemplate(compoundTypes: compoundTypes)
rowTemplatesTemp.append(compoundTemplate)
print("setting row templates \(rowTemplatesTemp)")
predicateEditor.rowTemplates = rowTemplatesTemp
predicateEditor.addRow(self)
}
The problem is that NSPredicateEditor does not allow NSExpressions with NSSubqueryExpressionType, so I can't have a subquery as my left expression. I need the NSPredicateEditorRowTemplate to include both the field name as well as the attribute path (fieldText, fieldDate or fieldNumber), so that the final predicate from one row would look like:
name == FIELDNAME && fieldText CONTAINS "<entered text>"
Any suggestions for how to do this? Should I be subclassing NSPredicateEditorRowTemplate, and if so how? Thanks in advance for any help.
Subclass NSPredicateEditorRowTemplate and override predicateWithSubpredicates to convert predicate "FIELDNAME CONTAINS <entered text>" to predicate "name == FIELDNAME && fieldText CONTAINS <entered text>".
I have a String data that stores date. Example:
let dates = ["1-Jan-2015", "1-Feb-2015", "20-Mar-2014", "15-Apr-2014", "12-May-2013", "23-Jun-2012"]
I need to do a count of how many times did that year occurs, and then store it. So what I require would be something like that
let years = ["2015" : 2, "2014" : 2, "2013" : 1, "2012" : 1]
I would like to avoid hard coding it for future growth. Meaning say if I have Year 2020, I cannot be hard coding all the years to store these values.
So the problem here is I've got no idea how should I do this on the overview. In addition, how do I convert the String format to date?
let dates = ["1-Jan-2015", "1-Feb-2015", "20-Mar-2014", "15-Apr-2014", "12-May-2013", "23-Jun-2012"]
// map the last 4 characters of your string
let years = dates.map{String($0.characters.suffix(4))}
// create a dictionary to store tue frequency
var frequencies:[String:Int] = [:]
// loop through the years
for year in years {
// increase it
frequencies[year] = (frequencies[year] ?? 0) + 1
}
let sortedFrequencies = frequencies.sort{ $0.0 < $1.0 }
print(sortedFrequencies) // [("2012", 1), ("2013", 1), ("2014", 2), ("2015", 2)]\n"
I have created the following solution. Use the following function and it will give you the following result in the screenshot
/// This function will return the array as per your requirement
func getyears(dates: NSArray) -> NSArray
{
var year = [String]()
let newdates = dates
var countvalue = 0
for i in dates {
let x = i.stringByReplacingOccurrencesOfString("-", withString: "")
let startIndex = x.endIndex.advancedBy(-4)
let lastFourDigitsOfdate = x.substringFromIndex(startIndex)
for xm in newdates
{
let xy = xm.stringByReplacingOccurrencesOfString("-", withString: "")
let startIndex1 = xy.endIndex.advancedBy(-4)
let lastFourDigitsOfdate1 = xy.substringFromIndex(startIndex1)
if lastFourDigitsOfdate == lastFourDigitsOfdate1
{
countvalue = countvalue + 1
}
}
year.append("\(lastFourDigitsOfdate) : \(countvalue)")
countvalue = 0
}
return getUniqArrayData(year)
}
// This function will be required to get an unque array
func getUniqArrayData<S: SequenceType, E: Hashable where E==S.Generator.Element>(source: S) -> [E] {
var seen: [E:Bool] = [:]
return source.filter { seen.updateValue(true, forKey: $0) == nil }
}
let dates = ["1-Jan-2015", "1-Feb-2015", "20-Mar-2014", "15-Apr-2014", "12-May-2013", "23-Jun-2012"]
//Calling the above function
getyears(dates)
Hope this will be helpful.