I'm trying to use the find function to get the index of a CKRecord in an array. However I'm running into a problem that I don't understand.
I have two arrays: notes and allNotes. notes is a subset of allNotes. I have an item from notes and I'm trying to find its index within allNotes. As you can see from the output it exists in both arrays, but its only being found in the notes array.
func removeNoteAtIndexPath(indexPath: NSIndexPath) {
let note = tableViewController.notes[indexPath.row]
if let index = find(allNotes, note) {
println("Found in allNotes")
} else {
println("Not found in allNotes")
}
if let index = find(tableViewController.notes, note) {
println("Found in notes")
} else {
println("Not found in notes")
}
println(tableViewController.notes[0])
println(allNotes[0])
}
Console output
Not found in allNotes
Found in notes
<CKRecord: 0x7fb49ad37120; recordType=Note, recordID=E6BD60A1-AB15-4952-84A3-81BDB4DFC961:(_defaultZone:__defaultOwner__), recordChangeTag=i8nh54t3, values={
Text = "My note";
}>
<CKRecord: 0x7fb49ac31f60; recordType=Note, recordID=E6BD60A1-AB15-4952-84A3-81BDB4DFC961:(_defaultZone:__defaultOwner__), recordChangeTag=i8nh54t3, values={
Text = "My note";
}>
You need to adapt Equatable protocol to CKRecord like below.
extension CKRecord: Equatable {}
public func
==( lhs: CKRecord, rhs: CKRecord ) -> Bool {
return lhs.recordID.recordName == rhs.recordID.recordName
}
I like Satachito's answer better, but this was my initial solution:
func findCKRecord(records: [CKRecord], record: CKRecord) -> Int? {
var index: Int?
let recordID = record.recordID
for (i, record) in enumerate(records) {
let currentRecordID = record.recordID
if recordID == currentRecordID {
index = i
}
}
return index
}
Related
class SomeClass {
var dates: [Date] {
get {
return ckRecord.object(forKey: "dates") as! [Date]
}
set(newDates) {
ckRecord.setObject(newDates as! CKRecordValue, "dates")
}
}
}
In the previous code, how do I write code in the get and set closures to save to CloudKit and retrieve the data from CloudKit everytime I get one of the values from the array or set one of the values in the array, which means I don't retrieve the whole array or set the whole array, only one of the values at a given index as in the following code:
var obj = SomeClass()
obj.dates[0] = Date()
I don't have a problem using CloudKit. I have a problem figuring out how to arrange the code for the get and set closures so that I properly access the array from the CloudKit record by index. I am attempting to wrap the CloudKit record in class SomeClass.
Any help will be appreciated.
I believe, you can't do that by implementing get/set for your property. But you can do that at least by two ways:
1) Extract logic of getter/setter in function:
func getDate(_ index: Int) -> Date?
func set(date: Date, index: Int)
This will work fine, but it looks ugly.
2) More swifty way is using the subscript. In this case you create class, that holds private dates and this class allows you to access concrete date by using subscript. Simple example:
class Dates {
private var dates: [Date] = []
subscript(index: Int) -> Date? {
get {
guard dates.indices.contains(index) else { return nil }
return dates[index]
}
set(newValue) {
guard let date = newValue else { return }
dates.insert(date, at: index)
}
}
}
My suggestion is an extension of CKRecord with functions to insert, add and get a date by index and index subscription.
To modify the array you always have to get it from the record, change it and put it back.
extension CKRecord {
func date(at index : Int) -> Date? {
guard let dates = self["dates"] as? [Date], index < dates.count else { return nil }
return dates[index]
}
func appendDate(_ date: Date) {
guard var dates = self["dates"] as? [Date] else { return }
dates.append(date)
self["dates"] = dates as CKRecordValue
}
func insertDate(_ date : Date, at index: Int) {
guard var dates = self["dates"] as? [Date], index <= dates.count else { return }
dates.insert(date, at: index)
self["dates"] = dates as CKRecordValue
}
public subscript(index: Int) -> Date? {
get {
guard let dates = self["dates"] as? [Date], index < dates.count else { return nil }
return dates[index]
}
set {
guard let newDate = newValue,
var dates = self["dates"] as? [Date],
dates.indices.contains(index) else { return }
dates[index] = newDate
self["dates"] = dates as CKRecordValue
}
}
}
I'm new in swift and I have some problem while I try to filter some categories in my UICollectionView.
Here my code to get all articles for all the categories.
func getArticlesforCategory(category: String) -> Int {
var result : Int = 0
for article in self.allArticles {
if category == article.category {
result += 1
}
}
return result
}
How can I filter only one single category, for example "test"?
I get all the categories by parsing xml from wordpress website
You can use filter function to filter your array :
func getArticlesforCategory(category: String) -> Int {
let filteredArray = allArticles.filter( {$0.category == category }) // Here you have filtered array
return filteredArray.count // If you want number of items pass count of filtered array
}
try below line:
let result = self.allArticles.filter { $0.category == "test" }.count
You can use filter, try this code
func getFilteredArray(category: String) -> [ array of your objects] {
let filteredArray = allArticles.filter( {$0.category == category })
return filteredArray
}
I have a structure in my Swift app:
open class Cluster : NSObject {
open var username: String? = ""
open var id: String? = ""
open var deleted: Bool? = false
}
and now I'm iterating over this array and I'm adding new elements to it, but only in case those elements are not there yet:
if(!self.array.contains(where: {$0.id==temp.id}))
{
self.array.append(temp);
}
I want to tweak this code so that it not only adds new elements if they're not there, but also removes the ones that - in the meantime - had their flag deleted changed to true.
I started writing this code:
if(!self.array.contains(where: {$0.id==temp.id}))
{
self.array.append(temp);
} else {
if(temp.deleted == true){
self.array.remove //how can I remove here this specific element?
}
}
To remove a particular element from an array, you are supposed to get index of that element first and then delete as shown below:
if let index:Int = self.array.index(where: {$0.id == temp.id && $0.deleted == true}) {
self.array.remove(at: index)
}
First, I suggest you fix your class:
An optional Bool makes no sense - the object is either deleted or not
An optional id doesn't make much sense either; All objects need an id
If you implement the hash and equality parts of NSObject then you get access to array's index(of:) method and you can use sets.
Cluster.swift
open class Cluster : NSObject {
open var username: String? = ""
open let id: String
open var isDeleted: Bool = false
init(id: String) {
self.id = id
}
open override var hashValue: Int {
get {
return self.id.hashValue
}
}
open override func isEqual(_ object: Any?) -> Bool {
guard let rhs = object as? Cluster else {
return false
}
let lhs = self
return lhs.id == rhs.id
}
}
Now, given an array of Cluster objects, you can remove the deleted ones using:
let cleanArray = dirtyArrayOfCluster.filter {
!$0.isDeleted
}
And you can remove duplicates by passing the array through a set:
let deDupedArray = Array(Set(cleanArray))
if temp.deleted == true, let index = array.index(where: { $0.id == temp.id }) {
array.remove(at: index)
}
What about this?
if array.contains(where: { $0.id == temp.id } ) {
array.append(temp)
}
array = array.filter { $0.deleted == true }
The first part add temp only if it is not into the array.
The last line removes all the elements marked as deleted.
I have a dictionary that's updated from another class. I have a property observer on the dictionary so I know when a value has been added or removed.
I create a sorted array based on the values of the dictionary. I need to keep this array updated and retain the index associated with the update for use with a UITableView. My UI is as such that a wholesale reloading of data isn't possible - I need to directly insert or remove rows based on what the update was.
I have simplified this into a playground:
func dictionaryUpdated() {
print("dictionary updated")
// Add or remove string at index depending on order.
}
var myDictionary : [Int:String] = ["Bob".hashValue:"Bob","Dave".hashValue:"Dave","Yoda".hashValue:"Yoda","Windu".hashValue:"Windu","Obi Wan".hashValue:"Obi Wan","Qui-gon".hashValue:"Qui-gon","Anakin".hashValue:"Anakin"] { didSet { dictionaryUpdated() } }
func addEntry(entry: String) {
myDictionary[entry.hashValue] = entry
}
func removeEntry(entry: String) {
myDictionary.removeValueForKey(entry.hashValue)
}
// sort the keys alphabetically while creating the array
var valuesArray = myDictionary.values.sort { (lhs, rhs) -> Bool in
return lhs < rhs
}
I have tried using an NSMutableOrderedSet but the keys can only be Strings.
Just playing around in playground. Can be much more elegant though...
var valuesArray: [String] = [] { didSet { valuesArray.sortInPlace { $0 < $1 } } }
func dictionaryUpdated(old: [Int: String]) {
let added = myDictionary.count > old.count
let item: [String] = added ? myDictionary.values.filter { !old.values.contains($0) } : old.values.filter { !myDictionary.values.contains($0) }
valuesArray += item
let index = valuesArray.indexOf(item[0])!
print("item " + (added ? "added" : "removed") + ": \(item) at index \(index)")
}
var myDictionary: [Int: String] = ["Yoda".hashValue: "Yoda", "Windu".hashValue: "Windu", "Obi Wan".hashValue: "Obi Wan"] {
didSet {
dictionaryUpdated(oldValue)
}
}
addEntry("Darth Vader")
print(valuesArray)
Output:
item added: ["Darth Vader"] at index 0
["Darth Vader", "Obi Wan", "Windu", "Yoda"]
Assuming you have the sorted array before and after the property change (which can be achieved via another instance variable), what you need to do is to compare the old and the new array, and detect which which indexes changed.
An elegant solution to this problem would be to add a diff method to the Array class which computes the difference. The method might look something like this:
extension Array where Element: Equatable {
func diff(other: [Element]) -> (added: [Int], deleted: [Int], moved: [(from: Int, to: Int)]) {
var added: [Int] = []
var deleted: [Int] = []
var moved: [(from: Int, to: Int)] = []
for (i, item) in enumerate() {
if let j = other.indexOf({$0 == item}) {
if i != j {
moved.append((from: i, to: j))
}
} else {
deleted.append(i)
}
}
for (i, item) in other.enumerate() {
if indexOf({$0 == item}) == nil {
added.append(i)
}
}
return (added: added, deleted: deleted, moved: moved)
}
}
You would then use like this: valuesArray.diff(oldValuesArray).
This method was working while I was using Swift 1.2. But now, I had to update to Xcode and I had to switch my language to Swift 2. This is the method from swift 1.2 which I used well ;
static func findById(idToFind : Int64) -> T? {
let query = table.filter(id == idToFind)
var results: Payment?
if let item = query.first {
results : T = Payment(id: item[id], imageName: item[image], type: item[type], deliveredPriceStr: item[deliveredPrice], orderID: item[orderId])
}
return results
}
Now I modified it for Swift 2 but couldn't manage;
static func findById(idToFind : Int64) -> T? {
let query = table.filter(id == idToFind)
do {
let query = try table.filter(id == idToFind)
let item = try SQLiteDataStore.sharedInstance.SADB.pluck(query)
if try item != nil {
let results : T = Payment(id: item[id], imageName: item[image], type: item[type], deliveredPriceStr: item[deliveredPrice], orderID: item[orderId])
} else {
print("item not found")
}
} catch {
print("delete failed: \(error)")
}
}
return results
}
And I'm getting this error : "Cannot subscript a value of type Row" . My item's data type seems like changed to Row. How can I parse it ? What should I do ?
PS: I'm using swift2 branch.
Finally I figured out how to get values. It's easy now the row item has get method on swift-2 branch. So new method is ;
static func findById(idToFind : Int64) -> T? {
let query = table.filter(id == idToFind)
var results : T?
do {
let query = table.filter(id == idToFind)
let item = SQLiteDataStore.sharedInstance.SADB.pluck(query)
if try item != nil {
results = Payment( id: (item?.get(id))!, imageName: (item?.get(image))!, type: (item?.get(type))!, deliveredPriceStr: (item?.get(deliveredPrice))!, orderID: (item?.get(orderId))!)
} else {
print("item not found")
}
}
catch {
print("delete failed: \(error)")
}
return results
}
Hope that this helps someone.