Iterate through an array in an array of dictionaries swift - arrays

I am currently in a bit of a bind.
struct sectionWithDatesAsName {
var sectionName : String
var sectionObjects : [SoloTransactionModel]!
init(uniqueSectionName: String?, sectionObject: [SoloTransactionModel]?) {
sectionName = uniqueSectionName ?? "nil"
if let section = sectionObject {
sectionObjects = section.reversed()
}
}
}
I currently have an array of sectionWithDatesAsName. And I can work with it, display in the tableView among other things.
The bind comes up when I want to check some information in the sectionObject before displaying it on the tableView.
I want to check the type of the sectionObject which is saved in the object itself.
How do I check the information in the sectionObject without slowing down the app? Or have a horrible time complexity calculated?
(Note: I can't change the format of the struct has this has already been used by a whole lot of other processes)

Write a function in your sectionWithDatesAsName with the signature filteredSections(type: sectionType) -> sectionWithDatesAsName
(If you don't have the ability to edit the definition of sectionWithDatesAsName you can create an extension that adds the above function)
If the sectionWithDatesAsName is defined elsewhere, define this function in an extension.
When you call it, build a new sectionWithDatesAsName object by filtering the arrays to match the specified type.
Use the resulting filtered sectionWithDatesAsName object as the data model for your table view. It will be built once and used for the lifetime of the tableView, so you will pay an O(n) time cost to filter it once when you create it.

Related

Error when trying to set array in userdefaults: Thread 1: "Attempt to insert non-property list object

I have solved the issue now, thanks for your help. I shouldn't have tried to save arrays with UITextViews, but I should have saved their text as strings instead. Here was the original question:
I have tried a lot, and googled a lot, but I can't solve this problem on my own. Whenever I try to save an array in userdefaults, it just is not working. I get the following error:
Thread 1: "Attempt to insert non-property list object (\n "<UITextView: 0x14001f800; frame = (0 0; 355 180); text = 'D'; clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600003f01d10>; layer = <CALayer: 0x6000031c83e0>; contentOffset: {0, 0}; contentSize: {355, 30}; adjustedContentInset: {0, 0, 0, 0}>"\n) for key content"
I don't know what a non-property list object is. And I do not know how to solve the problem. Below is the lines of code that do not work.
var contentList: [Any] = []
let cl = defaults.array(forKey: "content")!
if cl.count != 0{
contentList += cl
}
contentList.append(label)
defaults.setValue(contentList, forKey: "content")
If I take out the last line of code by turning it into a comment everything runs just fine. How should I replace that line of code? I essentially want to save an array of UITextViews and make it larger every time I call a fucntion (this code is part of a larger function). The reason why I have created another two lists (cl and contentList) is that it helps me with a problem down the line. What I cannot understand however, is why the last line of code doesn't work. If anyone has any ideas, please help me, it would be much appreciated.
Use only String as stated in comments :
var contentList: [String] = []
let cl = defaults.array(forKey: "content")!
if cl.count != 0{
contentList += cl
}
If lbText = label.text {
contentList.append(lbText)
defaults.setValue(contentList, forKey: "content")
}
You can only store a very limited list of data types into UserDefaults, commonly referred to as "property list objects" (Since property list (or plist) files will only store the same data types.
To quote the Xcode docs on UserDefaults, in the section titled "Storing Default Objects":
A default object must be a property list—that is, an instance of (or for collections, a combination of instances of) NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary [or Data, String, NSNumber, Date, Array, or Dictionary types in Swift.] If you want to store any other type of object, you should typically archive it to create an instance of Data.
(I added the equivalent Swift types to the above quote in square brackets, since it looks like Apple hasn't updated it for Swift.)
That's worded a little awkwardly. The idea is that you can only store data of the types listed. Because the Array and Dictionary types are "container" types, you can store any combination of arrays and dictionaries that contain combinations of any of the above types. For example, you can store an array that contains a dictionary, 3 dates, 2 floats, a Double, some Data, and 2 arrays, and those dictionaries and arrays can contain other dictionaries and/or arrays.)
It is almost always wrong to archive UIView objects like UITextViews. You should save the text properties of your text views instead.
If you want to manage a vertical stack of UITextView objects, I suggest adding a vertical stack view to your user interface, and then writing code that adds or removes UITextView subviews to your stack view. You should be able to find plenty of examples of adding and removing objects from stack views online. (It's really easy.)
If you want to manage a scrolling list of feeds of arbitrary length, you might want to use a table view or collection view instead. Those require that you set up a data model and implement a "data source". That takes a little practice to get right, but is very powerful.

Swift Array multiple appending on click

I'm creating a button that when clicked adds the current date and time to an array but when I try to append to the array it only appends once and not repeating the process
the Entries struct:
struct Enteries {
var dates:[String] = []
}
convert date to String:
func DateConverter(){
format.timeZone = .current
format.dateFormat = "yyyy-MM-dd HH:mm"
dateString = format.string(from: currentDate)
}
The function that appends: also its called later whenever an IBAction is triggered
func AddToDatabase () {
var entery = Enteries()
entery.dates.append(dateString)
print(entery.dates)
}
`
Yikes, there's a lot going on here.
First of all, Swift's convention is to use lowerCamelCase for functions. Only type names should be UpperCamelCase.
Secondly, function names should be verbs or verb phrases, type names should be nouns. If I saw DateConverter in some code, I would expect it to be a type. It's an UpperCamelCase noun, that's how types should be named. But yours is a function (which would be a total surprise to every other Swift developer, because it violates the expectations they've built up from Swift's naming conventions), that function should probably be called parseDate.
Which way does DateConverter convert? From String to Date, Date to String, or both? What's its input? What's it's output? These things should be obvious from a good function name, but are totally unknown here without looking at the implementation.
Critically, the DateConverter function doesn't take input from parameters, and doesn't return a result, instead it takes input from a side effect (accessing the variable currentDate) and returns a result via side effect (writing to an a variable dateString). This is really bad, for several reasons:
It's not reusable. You have no way to use this date parsing code somewhere else without copy/pasting it, which is how code duplication and complexity arise. If you ever decide to change the date format in your app, you won't have a central source-of-truth that you can change, instead you'll have to manually hunt down every copy of this function, and change it, hoping you don't miss any. Not good.
It's not thread safe
It's more complex than a simple function that has type (Date) -> String. It obfuscates what's going on.
It defies peoples' expectations, without justification.
Enteries.dates has a default value of [], which doesn't seem to be a good idea if you're going to be appending to it as soon as you create it. Instead, take the array via an initializer parameter.
Enteries.dates has type [String]. Why?! You already have Date objects, store those!
They're smaller (in memory)
They're presentation-agnostic, meaning you can properly format them for different interfaces and different locales at a later time, as necessary
They support date math. I often see people storing dates as strings, and ask questions like "How do I sort my array of dates?" (which are actually stored as strings), "How do I add 1 day to "2019-12-24", and they start doing funky parsing, splitting, joining, and it's all just an absolute mess
Here's how I would improve this code:
struct Entries {
var entries: [Entry]
}
struct Entry {
let date: Date
}
// Call this from your view layer, only when you're about to present a `Date` to a user.
func parse(date: Date) -> String {
let df = DateFormatter()
df.timeZone = .current
df.dateFormat = "yyyy-MM-dd HH:mm"
return format.string(from: currentDate)
}
var entries = Entries(entries: [])
func addToDatabase(entry: Entry) {
entries.append(entry)
print(enteries.entries)
}
you are creating a new entery object eveytime the function is called. SO its creating a new object everytime. Declare your entery object outside the function.
var entery = Enteries()
func AddToDatabase () {
entery.dates.append(dateString)
print(entery.dates)
}

Best way to utilize retrieved object's properties to populate arrays and subsequent labels?

I have a database (parse-server) from which I can fetch objects which contain information. Some of the information in the properties of the objects are used to populate labels on table views. The way I have been populating, let's say, the userName and userLike labels are as follows:
Appending Different Arrays with the objects properties
var userName = [String]()
var userLikes = [String]()
func query(){
let commentsQuery = PFQuery(className: "UserStuff")
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
for object in objects{
self.userName.append(object["userName"] as! String)
self.userLikes.append(object["userLikes"] as! String)
}
}
}
}
Ignore the fact that I don't have a .whereKey or any else statements to handle other cases... this is bare bones just for illustration of the question. Anyway, in this method, the userName and userLikes arrays are iterated through to populate the labels. The for object in objectss{} ensures that the indexes in one array (whether index 0,1,2,3,etc...) refers to/comes from the same object as the value in the index of the other array. However, I was wondering if would be better to do it as follows:
Appending the whole object to a PFObject array
var userObjects = [PFObject]()
func query(){
let commentsQuery = PFQuery(className: "UserStuff")
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
for object in objects{
self.userName.append(object)
}
}
}
}
With this method I could instead populate the labels with something like:
userNameLabel.text = String((userObjects[0])["userName"])
In this method all properties of the object would be accessible form the same array. I can see that this may have some advantages, but is this definitively the better way to do it/should I switch immediately?
I am going to say that the answer is that the latter of the two is probably the better method. This is because in the former, the information from a particular object is only linked between arrays by the order in the array. Any accidental or incorrectly scripted functions involving .append or .remove could skew the order between arrays and then an object's name might be the 3rd index in the nameArray but its likes may end up being the 4th index in the likesArray and it would be difficult to amend this issue. With the latter method, all information regarding an object's properties are linked to the object itself in the array and this issue is avoided.

Realm object in array?

Question
How can I create an array of objects containing Realm objects?
Code
let realm = try! Realm()
let data: [A] = realm.objects(A)
Error
Cannot invoke 'objects' with an argument list of type '(Object.type)'
How can I create an array of objects containing Realm objects?
From your code sample, I'll further assume that you want to make an array from a Realm Results, not just "standalone" Realm objects.
Since Results conforms to SequenceType, you can use SequenceType.map() to convert it into an array:
let arrayFromResults = results.map({ $0 })
Note, however, that this is almost always the wrong pattern to use.
From your tweet on the same topic, a preferable way to do this would be to encode what you want to display on screen as a Realm query:
self.results = realm.objects(A).filter("poppedOff == NO")
And "popping off" an object (whatever that means) would update the poppedOff property of that object.
Since Realm Results are auto-updating, this won't risk getting out of sync with the contents of the Realm, unlike the array conversion approach, which would have to be updated on every Realm change notification.

What are the differences between using JSON arrays vs JSON objects? [duplicate]

This question already has answers here:
Difference between JSONObject and JSONArray
(9 answers)
Closed 6 years ago.
What are the difference and advantages of using JSON arrays:
{
thing:[
{ },
{ }
]
}
versus JSON objects:
{
thing:{
{ },
{ }
}
}
The difference between an array and an object is that
Objects are set up using a key and value like:
person.age = 15;
If the key value is a variable, then one could access it like:
var key = "age";
alert(person[key]);
Arrays use an integer[1] index and take a value.
player[1].score += 1000;
[1] Yes, I know, in JavaScript the integer index is really turned into a string behind the scenes. Ignore that. Think of arrays taking an integer value ESPECIALLY when you think of JSON.
Objects- key and value, Arrays- integer. When do you use this or that?
I think of arrays and objects as "is a/an" and "has a" respectively.
Lets use "Fruit" as example.
Every item in fruit array is a type of fruit.
array fruit : [orange, mango, banana]
.
Arrays can contain objects,strings, numbers, arrays, but lets deal with only objects and arrays.
array fruit : [orange:[], mango:{}, banana:{}]
.
You can see that orange is an array too. It implies any item that goes int orange is a type of orange, say: bitter_orange, mandarin, sweet_orange.
for fruit object, any item in it is an attribute of fruit. thus the fruit has a
object fruit :{seed:{}, endocarp:{},flesh:{}}
This also implies that anything within the seed object should be property of seed, say: colour,
JSON arrays represent a collection of objects. In JS, theres a bunch of collection functions off of them such as slice, pop, push. Objects have just more raw data.
The second form you show is actually not valid JSON, as each of the objects in the "thing" object would need some sort or property name to access it by.
To answer your question, the difference is that in the first case, you would access the objects in "thing" using array access like obj.thing[0] or obj.thing[1]. In the second case, if you had proper property declarations you would access like obj.thing.property
Generally in JSON array are used to store a grouping of like items, while object are used to contain grouping of different properties for a single item.
JSON is primarily a language that allows serializing javascript objects into strings. So upon deserializing a JSON string you should get a javascript object structure. If your json deserializes into an object that stores 100 objects called object1 to object100 then that's going to be very inconvenient.
Most deserializers will expect you to have known objects and arrays of known objects so that they can convert the strings into the actual object structure in the language you're using.
Also this is a question that the philosophy of object oriented design would answer you.
A JSON object can be transformed using toJSON:
function kryptonite(key)
{
var replacement = {};
for(var __ in this)
{
if(__ in alias)
replacement[__] = this[__]
}
return replacement;
}
var alias = {"Clark":"","phone":""};
var contact = {
"Clark":"Kent",
"Kal El":"Superman",
"phone":"555-7777"
}
contact.toJSON = kryptonite;
var foo = JSON.stringify(contact)
A JSON array can be transformed using map:
var contact = {
"Clark":"Kent",
"Kal El":"Superman",
"phone":"555-7777",
"home":[{"present":"Metropolis"},{"past":"Krypton"},{"future":"Phantom Zone"}]
}
var filter = {"past":"","future":""}
function junction(value, index)
{
for (var __ in filter) if(value[__]) return value[__]
}
var island = contact.home.map(junction);

Resources