Nested Swift Dictionaries and Arrays like a Hash Perl - arrays

I'm having a horrible time understanding a few things about Swift.
I am attempting to do scripting in Swift to see if it might be useful. One of the major runs of scripting file is how easy the language gets data in from text-files and the hash/dictionary/array parsing syntax.
In trying to parse huge csv files, I'm trying to create a matrix Dictionary that mimics a perl method ${$hash}{$var1}{$var2}{$var3} = $value; where #house_hash is the actual hash and each of the keys are dynamically created and added if not present already.
in ObjectiveC, this was simple enough, using NSMutableDictionary, atleast the retrieval part of it.
Swift seems pretty exciting, i wonder with how much ease can this be done?
Till now, all I have is:
var hash = [Int : [Int : [String : AnyObject]]] ()
let number = hash[var1]?[var2]?["var3"]? .hashValue
var d3 = [String : AnyObject]()
var d2 = [Int : [String : AnyObject]] ()
d3["var3"] = var3 //var3 is a float
if(number == nil)
{
d2[var2] = d3
hash[var1] = d2
}
else
{
hash[var1]![var2] = d3
}
Absolutely no idea what I'm doing. There are no good examples if this dynamic nature of Swift dictionaries that house unknown object types (could be arrays, Ints, Strings, another Dictionary)..
UPDATE: I found this SO answer. But it does not seem to work in Swift Playground.

Related

how i can use ListOfArrays in Kotlin

I have been working mainly in JAVA but now I need to program some small things in kotlin. Among other things I am trying to convert the result of a database query into a list of array. The result of the database query has 4 columns, the number of rows I can not predict.
I have tried the following:
var output: mutableList<List<String>>
var output = mutableListOf<String>()
var output = mutableListOf<ArrayList>
List<List<String>> listOfLists = new ArrayList<List<String>>()
What I would like to do is this:
output.add(arrayOf("Field1", "Filed2", "Field3", "Field4"))
It can't be that hard, can it?
A list of arrays can be expressed as List<Array<T>>.
So if you want a mutable list to which you can add arrays of strings, simply do:
var output = mutableListOf<Array<String>>()
output.add(arrayOf("Field1", "Filed2", "Field3", "Field4"))
That being said, why do you want to use arrays? It's generally more convenient to work with lists, unless you're constrained by another API.
In kotlin you should use lists where you can. What you are trying to create is:
val output = mutableListOf<List<String>>()
output.add(listOf("Field1", "Filed2", "Field3", "Field4"))
If you are iterating through some other list to create your data for output you could do something like:
val otherList = listOf<String>("a", "b", "v")
val output = otherList.map { otherListData ->
listOf(otherListData + 1, otherListData + 2, otherListData + 3, otherListData + 4)
}
In which case you would only have immutable lists.

Search and replace string in 2D Array in Swift

Teaching myself swift, so complete noob here, but I'm far into a project and just know there must be an easier way to achieve something.
I have a 2D array:
var shopArray = [
["theme":"default","price":0,"owned":true,"active":true,"image":UIImage(named: "defaultImage")!,"title":"BUY NOW"],
["theme":"red","price":1000,"owned":false,"active":false,"image":UIImage(named: "redImage")!,"title":"BUY NOW"],
["theme":"blue","price":2000,"owned":false,"active":false,"image":UIImage(named: "blueImage")!,"title":"BUY NOW"],
["theme":"pool","price":3000,"owned":true,"active":false,"image":UIImage(named: "blueImage")!,"title":"BUY NOW"],
["theme":"line","price":4000,"owned":false,"active":false,"image":UIImage(named: "lineImage")!,"title":"BUY NOW"],
["theme":"neon","price":5000,"owned":false,"active":false,"image":UIImage(named: "lineImage")!,"title":"BUY NOW"]]
Where I simply want to create a function that runs and search for all the "owned" keys and make them all "false".
How do you search and replace in Arrays / 2D Arrays. More specifiaclly, what should the func look like?
Thank you!
You don't have a 2D array, you have an Array of Dictionaries.
You can set all of the values for the owned keys by iterating the indices of the Array and updating the values:
shopArray.indices.forEach { shopArray[$0]["owned"] = false }
That is the functional way to do it. You could also do the same operation with a for loop:
for idx in shopArray.indices {
shopArray[idx]["owned"] = false
}
You could do something like this to loopthrough the array replacing the approriate element.
var i = 0
for x in shopArray {
var y = x
y["owned"] = false
shopArray.remove(at: i)
shopArray.insert(y, at: i)
i = i + 1
}
or you could use a while loop to do the same with less code lines.
var y = 0
while y < shopArray.count {
shopArray[y].updateValue(false, forKey: "owned")
y += 1
}
There is proably somthing doable with .contains, but I'm not sure you need that toachive the result you mention above. Play around in a play ground in xcode and try a few different options without doing anything that might cause issues in your project.

Best way to find and match two integers from different arrays in swift 3? (Multidimensional Array)

In Swift 3 i have two multi dimensional arrays named json and json2 that gets some code from an api. I then do an for each on the first array, and for each value inside that array i loop over array 2 to find matches, this seems like a really bad way of doing this, i was hoping for an alternative way that's probally quicker?
Heres my code:
var json:[[String:Any]] = []
var json2:[[String:Any]] = []
for each in self.json {
for each2 in self.json2 {
if(each["PrePaidCoffeeCardId"]! as! Int == each2["id"]! as! Int)
{
print(each2["coffeeBrandId"]! as! Int)
}
}
}
this returns the output in the console:
2
2
3
this is the values i want to get, but the approach of getting them seems to take alot of power, i was hoping for an easier solution?
If you have large numbers of prepaid cards, you will get good performance by building a temporary index using a dictionary :
var cardBrands:[Int:[Int]] = [:]
json2.map{ ($0["id"]! as! Int, $0["coffeeBrandId"]! as! Int) }
.forEach{ cardBrands[$0] = [$1] + (cardBrands[$0] ?? Array<Int>()) }
let brandIds = json.flatMap{ cardBrands[ $0["PrePaidCoffeeCardId"]! as! Int]! }
The cardBrands dictionary is setup only once, and returns the list of coffeeBrandIds of each card id. Because of the dictionary's internal indexing it will provide much faster results than iterating through the json2 array. This will give you roughly N+M processing time instead of N*M.

How to get a specific limit of item in an array using swift?

As title says (not sure about my english). I am new to swift language.
I am currently working in a swift app where a a user pick the correct answer in a randomize questions.
I am using gameplaykit for randomizing
var questions = ["question1", "question2", "question3", "question4", "question5"]// my question array
func askQuestion(){
countries = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: countries) as! [String]//to randomize
...
}
So my app are working fine in randomizing all the question1...questionN repeateadly but what i want is to have a limit i.e. only 5 questions will be given.
I searched but found "getting the specific index of an array", not what i'm looking for.
Could you help me how to achieve this? or Is my approach right?
Thanks
You can use prefix(n) on an array to get the first n elements, such as:
countries = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: countries).prefix(3) as! [String]
You can get first N numbers of elements using Prefix. Here is an example
let n = 5 // could be any number of your choice
let firstNNumbers = questions.prefix(n)
Try this -
func askQuestion() {
countries = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: countries) as! [String]
//After randomize your array use prefix method -
let limit = 5
let updatedCountries = Array(countries.prefix(limit)) // Use this `updatedCountries` array to display in UI.
}

Array Contains Too Slow Swift

I have been porting over an algorithm I've been using in Java (Android) to Swift (iOS), and have run into some issues with speed on the Swift version.
The basic idea is there are objects with depths (comment tree), and I can hide and show replies from the dataset by matching against a list of hidden objects. Below is a visualization
Top
- Reply 1
- - Reply 2
- - Reply 3
- Reply 4
and after hiding from the dataset
Top
- Reply 1
- Reply 4
The relevant methods I've converted from Java are as follows
//Gets the "real" position of the index provided in the "position" variable. The comments array contains all the used data, and the hidden array is an array of strings that represent items in the dataset that should be skipped over.
func getRealPosition(position: Int)-> Int{
let hElements = getHiddenCountUpTo(location: position)
var diff = 0
var i = 0
while i < hElements {
diff += 1
if(comments.count > position + diff && hidden.contains(comments[(position + diff)].getId())){
i -= 1
}
i += 1
}
return position + diff
}
func getHiddenCountUpTo(location: Int) -> Int{
var count = 0
var i = 0
repeat {
if (comments.count > i && hidden.contains(comments[i].getId())) {
count += 1
}
i += 1
} while(i <= location && i < comments.count)
return count
}
This is used with a UITableViewController to display comments as a tree.
In Java, using array.contains was quick enough to not cause any lag, but the Swift version calls the getRealPosition function many times when calling heightForRowAt and when populating the cell, leading to increasing lag as more comment ids are added to the "hidden" array.
Is there any way I can improve on the speed of the array "contains" lookups (possibly with a different type of collection)? I did profiling on the application and "contains" was the method that took up the most time.
Thank you
Both Java and Swift have to go through all elements contained in the array. This gets slower and slower as the array gets larger.
There is no a priori reason for Java to fare better, as they both use the exact same algorithm. However, strings are implemented very differently in each language, so that could make string comparisons more expensive in Swift.
In any case, if string comparison slows you down, then you must avoid it.
Easy fix: use a Set
If you want a simple performance boost, you can replace an array of strings with a set of strings. A set in Swift is implemented with a hash table, meaning that you have expected constant time query. In practice, this means that for large sets, you will see better performance.
var hiddenset Set<String> = {}
for item in hidden {
strset.insert(item)
}
For best performance: use a BitSet
But you should be able to do a whole lot better than even a set can do. Let us look at your code
hidden.contains(comments[i].getId()))
If you are always accessing hidden in this manner, then it means that what you have is a map from integers (i) to Boolean values (true or false).
Then you should do the following...
import Bitset;
let hidden = Bitset ();
// replace hidden.append(comments[i].getId())) by this:
hidden.add(i)
// replace hidden.contains(comments[i].getId())) by this:
hidden.contains(i)
Then your code will really fly!
To use a fast BitSet implementation in Swift, include the following in Package.swift (it is free software):
import PackageDescription
let package = Package(
name: "fun",
dependencies: [
.Package(url: "https://github.com/lemire/SwiftBitset.git", majorVersion: 0)
]
)
i think you need the realPosition to link from a tap on a row in the tableview to the source array?
1) make a second array with data only for the tableViewDataSource
copy all visible elements to this new array. create a special ViewModel as class or better struct which only has the nessesary data to display in the tableview. save in this new ViewModel the realdataposition also as value. now you have a backlink to the source array
2) then populate this TableView only from the new datasource
3) look more into the functional programming in swift - there you can nicer go over arrays for example:
var array1 = ["a", "b", "c", "d", "e"]
let array2 = ["a", "c", "d"]
array1 = array1.filter { !array2.contains($0) }
or in your case:
let newArray = comments.filter{ !hidden.contains($0.getId()) }
or enumerated to create the viewmodel
struct CommentViewModel {
var id: Int
var text: String
var realPosition: Int
}
let visibleComments: [CommentViewModel] = comments
.enumerated()
.map { (index, element) in
return CommentViewModel(id: element.getId(), text: element.getText(), realPosition: index)
}
.filter{ !hidden.contains($0.id) }

Resources