Swift - How to map an array of objects with an function on the object - swift

I have an array of documents and want to use the toModel function on the document to transform all elements.
let documents = docs.map { $0.toModel() }
toModel() transforms the struct into another struct.
Is there a nicer, easier or more elegant way to do this?
I thought about something like let documents = docs.map(\.toModel)

Use a Model initializer that takes whatever a doc is as its argument. “to” methods are not Swifty because initializers are so versatile.
let documents = docs.map(Model.init)

Related

Convert Realm list of Strings to Array of Strings in Swift

I'm just starting up with RealmSwift, and I'm trying to store an array of Strings in Realm. It doesn't work, so now I'm using List<String>() as an alternative. However, how do I convert these Realm Lists back to [String] again? And if I can't do that, are there any alternatives?
Thanks
However, how do I convert these Realm Lists back to [String] again
You can simply cast List to Array, because List has Sequence Support:
let list = List<String>()
let array = Array(list)
Bear in mind that by converting to an array you'll lose the 'dynamic' quality of a Realm collection (i.e. you'll receive a static array, whereas keeping the original List will provide automatic updating should the source change). But you can create an array by using an extension, e.g.:-
extension RealmCollection
{
func toArray<T>() ->[T]
{
return self.compactMap{$0 as? T}
}
}
Then use:-
let stringList = object.strings.toArray()
Where object is the realm object, and strings is your field.
Here are the details. how to assign an array in the realm list model.
jim.dogs.append(objectsIn: someDogs)

Realm Swift - Convert an array of results into an array of Ints

I want to convert the results from my Realm data into Int.
Here is an example of how I want to use this.
let results = realm.objects(Data.self)
print(results)
However the result is type Results<Data> and cannot be converted into a Int but the results is an Int.
Just to be clear I want an array of Int from my results
You can simply use Array(realm.objects(RealmType.self)), which will convert the Results<RealmType> instance into an Array<RealmType>.
However, there are other serious flaws with your code. First of all, neither of the last two lines will compile, since firstly realm.objects() accepts a generic input argument of type Object.Type and Data doesn't inherit from Object. You can't directly store Data objects in Realm, you can only store Data as a property of a Realm Object subclass.
Secondly, myArray[results] is simply wrong, since results is supposed to be of type Results<RealmType>, which is a collection, so using it to index an Array cannot work (especially whose Element type is different).
It appears that based on the number of results from the database, you want to select an object from the array.
You can get the number of items in an array with .count. Be certain that an object exists at the specified index in the array, or your application will crash!
let numberOfResults = results.count
if myArray.count > numberOfResults {
let object = myArray[numberOfResults]
print(object)
}

Modifying Realm values swift using map

I want to modify model data in Realm database in Swift App. Here is my code:
try! realm.write {
realm.objects(CompanyModel.self).map({ (model) in
model.isSelected = true
})
}
Idea is simple, iterate through models and change isSelected bool property to true. But look like it not work. Why?
You shouldn't use map if you want to modify the original Results instance and hence the model objects stored in your Realm. 'map' is not a mutating function, it iterates through your array (or in this case, Results), applies a transformation to each element of the sequence, then returns a new sequence containing the results of the transformation.
What you would actually need is the forEach function, which only iterates through the elements of an Array and executes the closure for each element,but doesn't return a new Array. I am currently not able to test it, but I since forEach is a member function of Array and not of NSFastEnumeration, from which Results inherits, I think you cannot use forEach on a Results instance, so you need to use a regular for loop to do this.
try! realm.write {
for model in realm.objects(CompanyModel.self) {
model.isSelected = true
}
}

func removing(Element) -> [T] {} on collection in swift

If you have a collection type in Swift (like a Set<T>) then you can remove something from the set like...
var s = // some set
s.remove(someElement)
and it will mutate s and remove the someElement from it.
However, this is a mutating function.
Is there a non-mutating function that would return a new set? Like...
let smallerSet = largerSet.removing(someElement)
I could use filter but then it turns this from a O(1) into an O(n) time problem.
If there isn't one already I can write one myself. Is there a convention for the name of a non-mutating function like this?
You can use subtracting function of the Set. For that you need to put that element to be deleted in another Set.
let elementToBeDeleted = Set(arrayLiteral: 1)
var wholeSet = Set(arrayLiteral: 1,2,3)
wholeSet = wholeSet.subtracting(element)
print(wholeSet) //This would print [2,3].
Hope this what you need to get things done.

swift function to iterate possibly reversed array

I'd like to create a function that will iterate over an array (or collection or sequence). Then I will call that function with an array, and the reversed version of the array (but efficiently: without creating a new array to hold the reverse).
If I do this:
func doIteration(points: [CGPoint]) {
for p in points {
doSomethingWithPoint(p)
}
// I also need random access to points
doSomethingElseWithPoint(points[points.count-2]) // ignore obvious index error
}
And if I have this:
let points : [CGPoint] = whatever
I can do this just fine:
doIteration(points)
But then if I do this:
doIteration(points.reverse())
I get 'Cannot convert value of type 'ReverseRandomAccessCollection<[CGPoint]> to expected argument type [_]'
Now, I DON'T want to do this:
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
even though it will work, because that will (correct me if I'm wrong) create a new array, initializing it from the ReverseRandomAccessCollection returned by reverse().
So I guess I'd like to write my doIteration function to take some sort of sequence type, so I can pass in the result of reverse() directly, but ReverseRandomAccessCollection doesn't conform to anything at all. I think I'm missing something - what's the accepted pattern here?
If you change your parameter's type to a generic, you should get the functionality you need:
func doIteration
<C: CollectionType where C.Index: RandomAccessIndexType, C.Generator.Element == CGPoint>
(points: C) {
for p in points {
doSomethingWithPoint(p)
}
doSomethingElseWithPoint(points[points.endIndex - 2])
}
More importantly, this won't cause a copy of the array to be made. If you look at the type generated by the reverse() method:
let points: [CGPoint] = []
let reversed = points.reverse() // ReverseRandomAccessCollection<Array<__C.CGPoint>>
doIteration(reversed)
You'll see that it just creates a struct that references the original array, in reverse. (although it does have value-type semantics) And the original function can accept this new collection, because of the correct generic constraints.
You can do this
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
or this
doIteration(points.reverse() as [CGPoint])
but I don't think there is any real difference by the point of view of a the footprint.
Scenario 1
let reversedPoints : [CGPoint] = points.reverse()
doIteration(reversedPoints)
Infact in this case a new Array containing references to the CGPoint(s) present in the original array is created. This thanks to the Copy-on-write mechanism that Swift used to manage structures.
So the memory allocated is the following:
points.count * sizeOf(pointer)
Scenario 2
On the other hand you can write something like this
doIteration(points.reverse() as [CGPoint])
But are you really saving memory? Let's see.
A temporary variable is created, that variable is available inside the scope of the function doIteration and requires exactly a pointer for each element contained in points so again we have:
points.count * sizeOf(pointer)
So I think you can safely choose one of the 2 solutions.
Considerations
We should remember that Swift manages structures in a very smart way.
When I write
var word = "Hello"
var anotherWord = word
On the first line Swift create a Struct and fill it with the value "Hello".
On the second line Swift detect that there is no real reason to create a copy of the original String so writes inside the anotherWord a reference to the original value.
Only when word or anotherWord is modified Swift really create a copy of the original value.