Search and replace string in 2D Array in Swift - arrays

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.

Related

How to merge 2 arrays of equal length into a single dictionary with key:value pairs in Godot?

I have been trying to randomize the values in an ordered array (ex:[0,1,2,3]) in Godot. There is supposed to be a shuffle() method for arrays, but it seems to be broken and always returns "null". I have found a workaround that uses a Fisher-Yates shuffle, but the resulting array is considered "unsorted" by the engine, and therefore when I try to use methods such as bsearch() to find a value by it's position, the results are unreliable at best.
My solution was to create a dictionary, comprised of an array containing the random values I have obtained, merged with a second array of equal length with (sorted) numbers (in numerical order) which I can then use as keys to access specific array positions when needed.
Question made simple...
In GDScript, how would you take 2 arrays..
ex: ARRAY1 = [0,1,2,3]
ARRAY2 = [a,b,c,d]
..and merge them to form a dictionary that looks like this:
MergedDictionary = {0:a, 1:b, 2:c, 3:d}
Any help would be greatly appreciated.
Godot does not support "zip" methodology for merging arrays such as Python does, so I am stuck merging them manually. However... there is little to no documentation about how to do this in GDScript, despite my many hours of searching.
Try this:
var a = [1, 2, 3]
var b = ["a", "b", "c"]
var c = {}
if a.size() == b.size():
var i = 0
for element in a:
c[element] = b[i]
i += 1
print("Dictionary c: ", c)
If you want to add elements to a dictionary, you can assign values to the keys like existing keys.

Find all similar values in an array and group together into a new array

The problem: I have an array of objects, where the object has an x value. Each object also needs a y value, to be plotted on a Scatterplot. The y value needs to be a number between a min height(.3) and a max height(.6), and be evenly spaced based on the number of equal x values.
For example, if I have an array of objects where [(x=3)(x=4)(x=3)(x=4)(x=5)(x=3)], after iterating through the array I should have a new array of objects that looks like this: [(x=3, y=.3)(x=3, y=.45)(x=4, y=.6)(x=4, y=.3)(x=4, y=.6)(x=5, y=.45)].
The Question: What is the best way to accomplish this performance wise? Are there any packages out there that will help me iterate through the array.
Currently I've created a nested forEach loop, but the code for this is long and feels dirty to me. Any suggestions would be great :)
This is in an angular 4 project (typescript).
You definitely need the map function:
Check documantation at https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Array/map
With map you can transform you array into a new array, this way:
var array = [{"x" : 3}, {"x" : 4}, {"x": 5}, {"x" : 3}];
var counter = 1;
var newArray = array.map(object => {
var calculatedY = counter;
counter++;
//just an example, you have to calculate your y based on your conditions
return { x : object.x, y : calculatedY }
})
console.log(newArray);

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) }

6- or more dimensional Arrays in Scala

I was looking into multi-dimensional Arrays in Scala and came across some easy ways to create multi-dimensional Arrays. Namely:
val my3DimensionalArray = Array.ofDim[Int](3,4,5)
//array with dimensions 3 x 4 x 5
Or even
val myFilledArray = Array.fill[Int](3,4,5)(0)
//Array of same dimension filled with 0's
However this only works for 1 - 5 dimensional Arrays:
val my6DimensionalArray = Array.ofDim[Int](3,3,3,3,3,3) //Error
So how do people usually deal with creating higher dimensinoal Arrays? Is this left to 3rd party libraries to implement, or are there other data structures that Scala encourages us to use instead of high dimensional Arrays?
// create array of 5-dim-array => 6 dim array
Array.tabulate(3)(_ => Array.ofDim[Int](3,3,3,3,3))
Array.ofDim implementation uses this this pattern. see https://github.com/scala/scala/blob/v2.11.6/src/library/scala/Array.scala#L216-234
If you really want arbitrary numbers of dimensions, you typically use a single flat array with a second array that indexes by dimension to get to the element you want. So, for instance,
class MultiArray(dims: Array[Int]) {
private val array = new Array[Double](dims.product)
private def arrayIndex(index: Array[Int]) = {
var i = index.length - 1
var k = index(i)
var n = 1
while (i > 0) {
n *= dims(i)
k += n * index(i-1)
i -= 1
}
k
}
def apply(index: Array[Int]) = array(arrayIndex(index))
def update(index: Array[Int], value: Double) {
array(arrayIndex(index)) = value
}
}
would be a start. There are various mathematics libraries that do this sort of thing (IIRC Apache Commons Math does I can't quickly find a Java mathematics library that does, but ImgLib2 uses similar techniques for image data (they do local chunking also)). It's not really a generally useful thing to do, which is why you tend to find it in maths libraries.

Find Specific Array Using a Loop

I'm incredibly new to swift and I'm sure this question has been asked before, but I have no idea of the terminology of what to search.
I've seen how to do this before, but not sure where. I want to be able to loop though my arrays using a count, like the below (but that is not working). So the last number of the array name changes depending on the count. So if myCount = 0 then myArray will equal array_0001_00 and if my count = 6 then myArray will equal array_0001_06, and so on.
I'm not sure if I'm missing something small or if I'm completely on the wrong track.
let array_0001_00 = [102,102,102,102,102,102,102,102]
let array_0001_01 = [112,112,112,112,112,112,112,112]
myArray = array_0001_0\(myCount)
Any help would be much appreciated. Thanks.
I'm currently using the below, which works, but is creating a mountain of code:
if myCount == 0 {
myArray = array_0001_00
} else if myBuilderCountY == 1 {
myArray = array_0001_01
}
I hope I haven't misunderstood - you have a number of arrays, and you want to select one of them using an index. That looks like selecting an element from an array:
let array_0001_00 = [102,102,102,102,102,102,102,102]
let array_0001_01 = [112,112,112,112,112,112,112,112]
let array_0001_02 = [122,122,122,122,122,122,122,122]
let array_of_arrays = [
array_0001_00,
array_0001_01,
array_0001_02
]
let index = 1
let myArray = array_of_arrays[index] // This assigns (a copy of) array_0001_01

Resources