Sort array in Swift Ascending with empty items - arrays

In Xcode (Swift) I have an array that is initialized to 100 empty items:
var persons = [String](count:100, repeatedValue: "")
With some functions I add content to the places in the array, starting at 0.
So for example my array is at some given moment:
["Bert", "Daniel", "Claire", "Aaron", "", "", ... ""]
With the dots representing the rest of the empty items. I use this function for sorting my array alphabetically:
persons = persons.sorted {$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
This gives me an array back like this:
["", "", ... , "Aaron", "Bert", "Claire", "Daniel"]
What I want is to sort my array alphabetically but not with the empty items at the front. I need to get an array back like:
["Aaron", "Bert", "Claire", "Daniel", "", "", ... , ""]
For my part, I do not want an array with empty items but I found I couldn't add a value to my array if I did not declare like a 100 items (the array won't be filled to a 100 items, that's for sure).
Can anyone help me out?

As #Antonio said, it looks like you need a descending order set of strings. Besides the Dictionary method in #Antonio's answer (which works great), you can also use NSMutableSet (bridged from Objective-C):
let personSet = NSMutableSet()
personSet.addObject("Aaron")
personSet.addObject("Daniel")
personSet.addObject("Claire")
personSet.addObject("Aaron")
personSet.addObject("Bert")
personSet.addObject("Bert")
personSet.addObject("Joe")
personSet.removeObject("Joe") // You can remove too of course
Which creates the set:
{(
Claire,
Aaron,
Daniel,
Bert
)}
Then, when you want the people as an Array, you can use the allObjects cast to a [String]:
personSet.allObjects as [String]
And you can sort it just like you're currently doing:
let people = (personSet.allObjects as [String]).sort {$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
Which makes people:
[Aaron, Bert, Claire, Daniel]
For those wondering how to sort the Array as originally stated in the question (Ascending but with empty strings at the end), that can be done with a little bit of custom logic in the sort function:
var persons = ["Bert", "Daniel", "Claire", "Aaron", "", "", ""]
persons.sort { (a, b) -> Bool in
if a.isEmpty {
return false
} else if b.isEmpty {
return true
} else {
return a.localizedCaseInsensitiveCompare(b) == .OrderedAscending
}
}
Result:
["Aaron", "Bert", "Claire", "Daniel", "", "", ""]

Reading comments in your question and other answers, I realize that you need a ordered set, containing unique values. There's no built in data structure in swift for that, but it can be easily be done by using a dictionary: simply use the string value as dictionary key, and a boolean as dictionary value - this ensures that keys are unique:
var persons = [String : Bool]()
persons["Bert"] = true
persons["Daniel"] = true
persons["Clair"] = true
persons["Clair"] = true
persons["Aaron"] = true
persons["Daniel"] = true
persons["Clair"] = true
You can quickly verify that with the above code the dictionary contains 4 elements only.
Next, obtain a copy of the dictionary keys (as an array):
var values = persons.keys.array
and sort it:
values.sort { $0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
Alternatively, if you want to stick with the fixed sized array, you can remove the empty items before sorting:
persons = persons
.filter( { $0.isEmpty == false } )
.sorted {$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }

I think you are confused about arrays. Swift arrays are not statically allocated structures which must be allocated and filled to maximum design capacity. Below is a crude example of how you can accomplish most of what you are expressing here. However, I really think that a dictionary is better suited to your needs.
var persons = [String]()
var inputData = ["Bert", "Daniel", "Bert", "Claire", "Aaron"]
for item in inputData {
var found = false
for existing in persons {
if existing == item {
found = true
break
}
}
if (!found) {
persons.append(item)
}
}
persons.sort{$0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
println(persons)

Related

Split a list into two based on the filters in swift

Having a list of countries name, and if I search for a country that starts with B, how do I split the search with all the countries that begin with B as a list and the remaining countries as another list and then combine the both so that the countries with "B" is at the top
You can use sorted(by:) to achieve your goals. You simply need to check if the current and the next element start with your search term using the prefix operator and in case the current element does start with it, but the next one doesn't, sort the current element before the next one, otherwise preserve current order.
extension Array where Element == String {
/// Sorts the array based on whether the elements start with `input` or not
func searchSort(input: Element) -> Array {
sorted(by: { current, next in current.hasPrefix(input) && !next.hasPrefix(input) })
}
}
["Austria", "Belgium", "Brazil", "Denmark", "Belarus"].searchSort(input: "B")
Output: ["Belgium", "Brazil", "Belarus", "Austria", "Denmark"]
Do you need it for a searchBar? If yes, try this:
let filteredCountries = CountriesArray.filter({ (country: String) -> Bool in
return country.lowercased().contains(searchText.lowercased()) && country.hasPrefix(searchText)
})
Also, for sorting try this:
let sortedArray = filteredCountries.sorted(by: { $0.lowercased() < $1.lowercased() })
Even if you don't need it for a searchBar, explore filter() method, its very helpfull and easy to use.
You can also use Dictionary for splitting, it won't sort the key, but you got separated arrays to use:
let a = ["FF0000", "000000", "0083FF", "FBE58D", "7A9EB0", "909CE1"]
let b = Dictionary(grouping: a, by: { $0.first! })
print(b) //["0": ["000000", "0083FF"], "7": ["7A9EB0"], "F": ["FF0000", "FBE58D"], "9": ["909CE1"]]

Create ordered Array from Dictionary

This is a fundamental thing that I should know but don't know at a deep level and therefore find confusing. Dictionaries seem to be unordered list of keys and values. I want to create an ordered list of keys and values so that I can sort and otherwise keep track of order. I think this means I have to convert the dictionary into a multi-dimensional Array that has the key and value and also an index value ie 0,1,2,3 etc.
If my dictionary looks like the following:
var myScores = [String: Float]()
myScores = ["player1":22,"player2":33]
How do I convert it into an Array where player1:22 is the first element and player2:33 is the second element?
Edit:
Alternative without creating struct is to create an empty array of dictionaries in the form of your dictionary and then append your dictionary to the array.
var myArray = [[String: Float]]()//note double brackets
propArray.append(myScores)
What you want is an array of structs.
struct Score {
let playerName: String
let score: Int
}
let scores = [Score(playerName: "player1", score: 22),
Score(playerName: "player2", score: 33),
]
let sortedScores = scores.sorted(by: { $0.playerName < $1.playerName })
To get key-value pairs out of a dictionary is straightforward, though it forces you to work with tuples, which are not a particularly friendly type. Even so, it's done this way:
let sortedScores = myScores.sorted { $0.key < $1.key }
That will create:
[(key: "player1", value: 22.0), (key: "player2", value: 33.0)]
Or
for (name, score) in myScores.sorted(by: { $0.key < $1.key }) {
print(name, score)
}
You can keep the dictionary as-is. One possible solution is to create an array that contains just the keys in the desired order. Then you can iterate the key array and access the elements of the dictionary.
var myScores: [String:Float] = ["player1":22, "player2":33]
var myPlayers = myScores.keys.sorted()
for player in myPlayers {
let score = myScores[player]
}
This works when you want to show the data in some particular order, such as in a table view.

How to map an array of integers as keys to dictionary values

If I have the following array and I want to create a string array containing the door prizes by mapping the mysteryDoors array as keys for the dictionary, and get the corresponding values into a new array.
Thus I would get a string array of ["Gift Card", "Car", "Vacation"] how can I do that?
let mysteryDoors = [3,1,2]
let doorPrizes = [
1:"Car", 2: "Vacation", 3: "Gift card"]
You can map() each array element to the corresponding value in
the dictionary. If it is guaranteed that all array elements are
present as keys in the dictionary then you can do:
let mysteryDoors = [3, 1, 2]
let doorPrizes = [ 1:"Car", 2: "Vacation", 3: "Gift card"]
let prizes = mysteryDoors.map { doorPrizes[$0]! }
print(prizes) // ["Gift card", "Car", "Vacation"]
To avoid a runtime crash if a key is not present, use
let prizes = mysteryDoors.flatMap { doorPrizes[$0] }
to ignore unknown keys, or
let prizes = mysteryDoors.map { doorPrizes[$0] ?? "" }
to map unknown keys to a default string.
If you have to use map, then it would look something like the following:
let array = mysteryDoors.map { doorPrizes[$0] }
After re-reading the Apple Doc example code and changing it to what I need. I believe this is it (Almost). Unfortunately it is now in string format with new lines...
let myPrizes = doorPrizes.map { (number) -> String in
var output = ""
output = mysteryDoors[number]!
return output;
}

Swift 3 : Remove value in Array with Unknown index

I want to implement a multiple click in my Shinobi DataGrid. I have a grid which have array
( ["1", "32", and more] )
If I click the grid I put it into new Array self.arrayNr.append(currNr).
But I want to check and remove if currNr is already exist in arrayNr it is will be remove from the arrayNr.
I'm new and using Swift 3. I read some question regarding with my question like this and this but it's not working. I think the Swift 2 is simpler than Swift 3 in handling for String. Any sugesstion or answer will help for me?
You can use index(of to check if the currNrexists in your array. (The class must conform to the Equatable protocol)
var arrayNr = ["1", "32", "100"]
let currNr = "32"
// Check to remove the existing element
if let index = arrayNr.index(of: currNr) {
arrayNr.remove(at: index)
}
arrayNr.append(currNr)
Say you have an array of string, namely type [String]. Now you want to remove a string if it exists. So you simply need to filter the array by this one line of code
stringArray= stringArray.filter(){$0 != "theValueThatYouDontWant"}
For example, you have array like this and you want to remove "1"
let array = ["1", "32"]
Simply call
array = array.filter(){$0 != "1"}
Long Solution
sampleArray iterates over itself and removes the value you are looking for if it exists before exiting the loop.
var sampleArray = ["Hello", "World", "1", "Again", "5"]
let valueToCheck = "World"
for (index, value) in sampleArray.enumerated() {
if value == valueToCheck && sampleArray.contains(valueToCheck) {
sampleArray.remove(at: index)
break
}
}
print(sampleArray) // Returns ["Hello", "1", "Again", "5"]
Short Solution
sampleArray returns an array of all values that are not equal to the value you are checking.
var sampleArray = ["Hello", "World", "1", "Again", "5"]
let valueToCheck = "World"
sampleArray = sampleArray.filter { $0 != valueToCheck }
print(sampleArray) // Returns ["Hello", "1", "Again", "5"]

Swift 3 - Difference between two arrays of dictionary

I have two arrays of dictionaries:
let arrayA = [["name1": "email1"], ["name2": "email2"]]
let arrayB = [["name1": "email1"], ["name3": "email3"]]
I want to compare them and get another two arrays: arrayC should have the elements in arrayA but not in arrayB, and arrayD should have the elements in arrayB but not in arrayA:
let arrayC = [["name2": "email2"]]
let arrayD = [["name3": "email3"]]
How can I do this taking into the consideration large arrays?
Here you go
let arrayA = [["name1": "email1"], ["name2": "email2"]]
let arrayB = [["name1": "email1"], ["name3": "email3"]]
let arrayC = arrayA.filter{
let dict = $0
return !arrayB.contains{ dict == $0 }
}
let arrayD = arrayB.filter{
let dict = $0
return !arrayA.contains{ dict == $0 }
}
I know this answer may be complicating things and that you can use filter but...have you considered using Sets instead of Arrays?
Sets can give you operations for finding elements in setA but not in setB or elements in setA and setB out of the box.
There are a few caveats about sets though. As it says in The Swift Programming Guide
A set stores distinct values of the same type in a collection with no defined ordering. You can use a set instead of an array when the order of items is not important, or when you need to ensure that an item only appears once.
notice from the above:
Distinct: Meaning no duplicates
No definded ordering: Means that you cannot expect your sets to be in order
Also, notice this (also from The Swift Programming Guide):
A type must be hashable in order to be stored in a set—that is, the type must provide a way to compute a hash value for itself.
If you can live with that...then sets are a fine solution I think.
Here is an example...I created a simple Email struct and made that implement Hashable:
struct Email {
let name: String
let email: String
}
extension Email: Hashable {
var hashValue: Int {
return "\(name)\(email)".hashValue
}
static func ==(lhs: Email, rhs: Email) -> Bool {
return lhs.name == rhs.name && lhs.email == rhs.email
}
}
And that can then be used like so:
let arrayA = [Email(name: "name1", email: "email1"), Email(name: "name2", email: "email2")]
let arrayB = [Email(name: "name1", email: "email1"), Email(name: "name3", email: "email3")]
let setA = Set(arrayA)
let setB = Set(arrayB)
let inBothAAndB = setA.intersection(setB) //gives me an Email with "name1", "email1"
let inAButNotB = setA.subtracting(setB) //gives me an Email with "name2", "email2"
let inBButNotA = setB.subtracting(setA) //gives me an Email with "name3", "email3"
So...I don't know if that confuses things for you or makes things harder or maybe impossible (if you're data can contain more than one element with the same name and email for instance) but...I just thought you should consider sets :)
Hope that helps you.

Resources