Append unique values to an array in swift - arrays

I haven't found anything on that in Swift. Have found how to find unique values on an array, but not this. Sorry if it sounds quite basic...
But I have the following array
var selectedValues = [String]()
And the following value that comes from a Parse query
var objectToAppend = object.objectForKey("description")! as! String
this is how I'am doing it at the moment.
self.selectedHobbies.append(objectToAppend)
But because the query happens repeated times, it ends up appending repeated values. It works, but I just want to not waste memory and only keep unique values.
Any ideas on how to solve that in swift?

You can use a Set which guarantees unique values.
var selectedValues = Set<String>()
// ...
selectedValues.insert(newString) // will do nothing if value exists
Of course, the elements of a Set are not ordered.
If you want to keep the order, just continue with the Array but check before you insert.
if !selectedValues.contains("Bar") { selectedValues.append("Bar") }

I guess that your problem was resolved but I add my answer for next developers who's facing same problem :)
My solution is to write an extension of Array to add elements from an array with a distinct way:
here the code :
extension Array{
public mutating func appendDistinct<S>(contentsOf newElements: S, where condition:#escaping (Element, Element) -> Bool) where S : Sequence, Element == S.Element {
newElements.forEach { (item) in
if !(self.contains(where: { (selfItem) -> Bool in
return !condition(selfItem, item)
})) {
self.append(item)
}
}
}
}
example:
var accounts: [Account]
let arrayToAppend: [Account]
accounts.appendDistinct(contentsOf: arrayToAppend, where: { (account1, account2) -> Bool in
return account1.number != account2.number
})

Related

how do I get rid of this simple SwiftUI error?

I just wanna get all the records from the transaction variable and save it to an array.i tried and all I am getting is this constant error. please help me, I just wanna all models(records) to be saved on an array.
Type '()' cannot conform to 'View'
#State private var transactions: [Transactions] = [Transactions]()
ForEach(transactions, id: \.self) { transaction in
timexxx[0] = transaction.timexx ?? "0"
Text(timexxx[0] ?? "0")
}
enter image description here
Like what #multiverse has suggested
ForEach loop expects some sort of View but you are giving it or attempting to give an "array" (it only wants View)
Here is an updated code where you give the ForEach what it wants and you append to your timexxx array
ForEach(Array(transactions.enumerated()), id: \.offset) { (offset, transaction) in
Text(transaction.timexx ?? "\(offset)")
.onAppear {
timexxx[offset] = transaction.timexx ?? "\(offset)"
}
}
Update
for your question
"how do I do this with a simple "For" loop ? let's say I wanna do this operation in a simple class."
This is how it's done.
I removed the view Text.
for (i, transaction) in transactions.enumerated() {
timexxx[i] = transaction.timexx ?? "0"
}
Ok , so this is an error i faced as well, when i was learning SwiftUI( i am still a beginner), so now we need to understand , what does this error actually means, in this case the ForEach loop expects some sort of View but you are giving it or attempting to give an "array" (it only wants View)....
If you want values to be transferred to an array just simply create a function and do it .....
say you have a class and inside of which you do
#Published var song = [Song]()
then what you do is inside a function like loadData()
objects is the array whose elements you want transferred and most likely those elements belong to a Struct like Song here(if not its even simpler just use what ever type it has like Int, String etc.), this way all your elements will get transferred to song from objects
func loadData() {
song = objects.map {
artist in
Song(album: artist.album, artistImage: artist.artistImage)
}
}
Here i add the simplest possible way to transfer from one array to other
var objects = [1,2,3,4,5]
var song = [Int]()
func loadData() {
song = objects.map { $0 }
}
loadData()
print(song)
//[1,2,3,4,5]

filter/identify dictionary keys that have matching values

Not sure if I need reduce, sorting, filtering or using the newer unique methods. I've even tried Equatable solutions.
I need to auto-identify keys that have matching values and take only those keys and put them into a new dictionary or array.
var userDB: [String:Any] = ["userID1":"gameIDa", "userID2":"gameIDa", "userID3":"gameIDc", "userID4":"gameIDd", "userID5":"gameIDe"]
As you can see only these two IDs have the same value of gameIDa. I need output of this result.
// [userID1, userID2]
Thanks
You can use Dictionary(grouping:by:) to achieve this easily and then reduce it to a dictionary which contains only entries with multiple values.
var userDB: [String: String] = ["userID1":"gameIDa", "userID2":"gameIDb", "userID3":"gameIDc", "userID4":"gameIDa", "userID5":"gameIDe"]
let dict = Dictionary(grouping: userDB.keys) { userDB[$0] ?? "" }.reduce(into: [String:[String]](), { (result, element) in
if element.value.count > 1 {
result[element.key] = element.value
}
})
dict
["gameIDa": ["userID1", "userID4"]]
Firstly, in order to be able to compare values, the Dictionary Value type needs to be an Equatable one. Once this is fulfilled, you can easily filter the keys that hold the queried value:
extension Dictionary where Value: Equatable {
func keysWithCommonValues() -> [Key] {
// go over all keys and keep ones for which the dictionary has one another key with the same value
return keys.filter { key in contains { $0.key != key && $0.value == self[key] } }
}
}
// userDB is not inferred as [String:String]
var userDB = ["userID1":"gameIDa", "userID2":"gameIDa", "userID3":"gameIDc", "userID4":"gameIDd", "userID5":"gameIDe"]
print(userDB.keysWithCommonValues()) // ["userID1", "userID2"]

Sort an object array [Venue] by its property using a sorted array [Geofire Keys] as reference?

I have built the following "helper" function, which takes as parameters:
'unsortedArray': The array of Venue objects required to be sorted by its .venueID string property
'sortingGeoArray': the Geofire string keys to be used as reference to order the unsorted array above.
and it returns a sorted array of type [Venue] via an escaping handler.
I have tried to implement this nice and simple solution suggested on the following thread:
'Sorting a Swift array by ordering from another array'
func sortVenuesArraybyGeofireKeys(unsortedArray: [Venue], sortingGeoArray: [String] , handler: #escaping (_ sortedArray: [Venue]) -> ()){
let ordering = Dictionary(uniqueKeysWithValues: sortingGeoArray.enumerated().map { ($1, $0) })
let sorted: [Venue] = unsortedArray.sorted{ ordering[$0.venueID]! < ordering[$1.venueID]! }
handler(sorted)
}
I have tried the sorting code above in multiple places through out my code by I always get a "Unexpectedly found nil while unwrapping an Optional value" at the following line (works when i test it an playground):
let sorted: [Venue] = unsortedArray.sorted{ ordering[$0.venueID]! < ordering[$1.venueID]!
On my debug window below, I have a feeling that the .map function into the let 'ordering' is not working and therefore finding nil on the next line
any help would be appreciated.
UPDATE: thanks to the support below, it appears that my Geofire query below in particular the 'append' to venueGeoKeys [string] array is not appending the key values, hence why found nil when I execute the function to sort.
let query = self.GEOFIRE_VENUES.query(at: location, withRadius: 1000)
query.observe(.keyEntered) { (key: String!, location: CLLocation!) in
self.venueGeoKeys.append(key)
}

How to order PFObjects based on its creation date?

I have some user comments stored in a database (parse-server) that I would like to would like to display on my viewController's viewDidLoad(). I can easily pull the comment objects as follows:
super.viewDidLoad()
func query(){
let commentsQuery = PFQuery(className: "Comments")
commentsQuery.whereKey("objectId", equalTo: detailDisclosureKey)
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
if objects.count == 1{
for object in objects{
self.unOrderedComments.append(object)
}
}
}
}
}
This query dumps all of the of the comments in the unOrederedComments array. Each comment is added to the database with a createdAt property automatically being added relating the exact time of its creation. This property is a string with (as an example) the form: "2017-08-13T19:31:47.776Z" (the Z at the end is at the end of every string... not exactly sure why its there but its constant). Now, each new comment is added in order to the top of database and thus any queried result should be in order regardless. However, I would like to make sure of this by reordering it if necessary. My general thought process is to use .sorted, but I cannot figure out how to apply this to my situation
func orderComments(unOrderComments: [PFObject]) -> [PFObject]{
let orderedEventComments = unOrderedEventComments.sorted(by: { (<#PFObject#>, <#PFObject#>) -> Bool in
//code
})
}
This is the generic set up but I cannot, despite looking up several examples online figure out what to put in the <#PFObject#>'s and in the //code. I want to order them based on the "createdAt" property but this is not achieved via dot notation and instead requires PFObject["createdAt"] and using this notation keeps leading to error. I feel as so though I may need to set up a custom predicate but I do not know how to do this.
I was in the same situation, what I did was to first create an array of structs with the data I downloaded where I turned the string createdAt into a Date, then used this function:
dataArrayOrdered = unOrderedArray.sorted(by: { $0.date.compare($1.date) == .orderedAscending})
(.date being the stored Date inside my array of strcuts)
Try this code, notice that I assumed you have a variable name called ["Comments"] inside your Parse database, so replace if necessary. Also, I realised that createdAt it's in Date format, so there was no need to change it from String to Date, chek if it works the same for you, if it doesn't refer to this: Swift convert string to date.
struct Comment {
var date = Date()
var comment = String()
}
var unOrderedComments: [Comment] = []
var orderedComments = [Comment]()
override func viewDidLoad() {
super.viewDidLoad()
query()
}
func query(){
let commentsQuery = PFQuery(className: "Comments")
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
if objects.count >= 1{
for object in objects{
let newElement = Comment(date: object.createdAt!, comment: object["Comments"] as! String)
self.unOrderedComments.append(newElement)
print(self.unOrderedComments)
}
}
self.orderedComments = self.unOrderedComments.sorted(by: { $0.date.compare($1.date) == .orderedAscending})
print(self.orderedComments)
}
}
}

iOS Swift: How to find unique members of arrays of different types based on specific attributes

Goal: I have two different classes, and two arrays containing members of each class. Using Swift 2.0, I would like to find the unique members of one array compared to the other based on specific attributes of each class.
Example:
class A {
var name: String
init(name: String) {
self.name = name
}
}
class B {
var title: String
init(title: String) {
self.title = title
}
}
let aArray = [A(name:"1"), A(name:"2"), A(name:"3"), A(name:"4")]
let bArray = [B(title:"1"), B(title:"2"), B(title:"5")]
So, I'm looking for some operation between aArray and bArray which returns the 3rd and 4th element of aArray, because they are uniquely in aArray, where the basis of comparison is the attributes A.name and B.title.
Of course, reversing the order of the operation would pick out the 3rd element of bArray, because it is uniquely in bArray.
I know I can accomplish the goal straightforwardly using a simple for loop, but I was hoping for something more elegant and more optimized. But if a for loop is as fast or faster than anything fancier, I'm happy to use it just as well.
I'm not sure fancy or elegant this code is, but, we could do something like this:
let mappedArray = bArray.map { $0.title }
let filteredArray = aArray.filter { !mappedArray.contains($0.name) }
So when we want the unique elements from aArray, we first map the elements from bArray to get an array of the value we want to actually compare:
let mappedArray = bArray.map { $0.title }
mappedArray is just an array of strings based on the title property of the objects in bArray.
Next, we use the filter method to filter objects from aArray. The filter method returns an array with objects that pass the test in our closure. The test we want to apply is objects that are not contained in the mapped array we just built.
let filteredArray = aArray.filter { !mappedArray.contains($0.name) }
If we want to do it the other way, just change a few things:
let mappedArray = aArray.map { $0.name }
let filteredArray = bArray.filter { !mappedArray.contains($0.title) }

Resources