I have a multi-dimensional array. It gets like [["1","2","3"]], how can I access "1"?
To get more in detail...
lazy var data : [[NSURL]] = {
var array = [[NSURL]]()
if array.count == 0 {
var index = 0
var section = 0
for image in images {
if array.count <= section {
array.append([NSURL]())
}
array[section].append(image)
index += 1
}
}
return array
}()
With print(data), I can access:
[["firstUrl", "secondUrl", "thirdUrl"]]
How can I access "first"?
You can do it like this: array[0][0]
For example enumerate like this:
var dict = [["1","2","3"]]
for (index, item) in dict.enumerate() {
//hear you enumerate []
for (index, item) in item.enumerate() {
//hear you enumerate ["1","2","3"]
print("Found \(item) at position \(index)")
}
}
Related
I have an easy question that is also hard at the same time. I have two separate structs (this can also work for classes):
struct FBTweet {
var tweetId: Int? //set
var tweetText: String? //set
}
and
struct Status {
var statusId: Int? //set
var statusText: String? //no value
}
I have an array of both structs var fbTweetArray: [FBTweet] = [] and var statusArray: [Status] = []
I have set every variable in to a certain value in each index in fbTweetArray but I only set the .statusId variable in each index for statusArray. For every statusArray.statusId value in statusArray, there is only one fbTweetArray.tweetId that has the same exact Int value. I am trying to make is so that if these two variables are the same then I should set set
statusArray.statusText to whatever fbTweetarray.tweetText is. So for example only fbTweetArray[1].tweetid = 2346 and statusArray[4].statusId = 2346 have 2346 as their value. There for if fbTweetArray[1].tweetText = "hello friend" then statusArray[4].statusText needs to be set to "hello friend".
So far I have
func testWhat () {
var fbTweetArray: [FBTweet] = []
var statusArray: [Status] = []
for fbTweet in fbTweetArray {
for var status in statusArray {
if (status.statusId == fbTweet.tweetId ) {
status.statusText = fbTweet.tweetText
}
}
}
}
how do I set the for var status in the for loop back into the statusArray since it is now a var and is different than one of the indexes in var statusArray: [Status] = []
Basically, you need only one for/forEach loop to achieve what you want:
var fbTweetArray: [FBTweet] = [
FBTweet(tweetId: 1, tweetText: "1"),
FBTweet(tweetId: 2, tweetText: "2"),
FBTweet(tweetId: 3, tweetText: "3")
]
var statusArray: [Status] = [
Status(statusId: 2, statusText: nil),
Status(statusId: 1, statusText: nil),
Status(statusId: 3, statusText: nil)
]
fbTweetArray.forEach { tweet in
if let index = statusArray.index(where: { $0.statusId == tweet.tweetId }) {
statusArray[index].statusText = tweet.tweetText
}
}
print(statusArray.map { $0.statusText }) // [Optional("2"), Optional("1"), Optional("3")]
Note, that your ids in both structures can be nil. To handle this situation (if both id is nil - objects are not equal) you can write custom == func:
struct Status {
var statusId: Int? //set
var statusText: String? //no value
static func == (lhs: Status, rhs: FBTweet) -> Bool {
guard let lhsId = lhs.statusId, let rhsId = rhs.tweetId else { return false }
return lhsId == rhsId
}
}
...
// rewrite .index(where: ) in if condition
if let index = statusArray.index(where: { $0 == tweet }) { ... }
Also, there is some pro-tip. If you adopt your structs to Hashable protocol, you will be able to place FBTweets and Statuses into Set structure. The benefits of that:
If you instead store those objects in a set, you can theoretically
find any one of them in constant time (O(1)) — that is, a lookup on a
set with 10 elements takes the same amount of time as a lookup on a
set with 10,000.
You can find more in-depth info about it in a new great article by NSHipster.
Your question is interesting only if both the arrays are not ordered.
To find the element from fbTweet array, you can sort it and employ binary search.
Then enumerate status array and find the fbTweet object with the same identifier and modify the status object. It needs to be saved again in the array as structs get copied on write.
extension Array where Element == FBTweet {
func binarySearchFBTweetWith(_ id:Int) -> FBTweet? {
var range = 0..<self.count
while range.startIndex < range.endIndex {
let midIndex = range.startIndex + (range.endIndex - range.startIndex) / 2
guard let tweetId = self[midIndex].tweetId else {
continue
}
if tweetId == id {
return self[midIndex]
} else if tweetId < id {
range = midIndex+1..<range.endIndex
} else {
range = range.startIndex..<midIndex
}
}
return nil
}
}
fbTweetArray.sort{($0.tweetId ?? 0) < ($1.tweetId ?? 0)}
for (index, status) in statusArray.enumerated() {
guard let statusId = status.statusId else {continue}
guard let fbTweet = fbTweetArray.binarySearchFBTweetWith(statusId) else {continue}
var status = status
status.statusText = fbTweet.tweetText
statusArray[index] = status
}
An alternative would be use dictionaries instead of arrays, for better performance and easier implementation (in this case). You can easily get the array of keys and values later If you need.
In this case, the Id would be the Key of the dictionary, and the text the value.
I'm new in swift and I have some problem while I try to filter some categories in my UICollectionView.
Here my code to get all articles for all the categories.
func getArticlesforCategory(category: String) -> Int {
var result : Int = 0
for article in self.allArticles {
if category == article.category {
result += 1
}
}
return result
}
How can I filter only one single category, for example "test"?
I get all the categories by parsing xml from wordpress website
You can use filter function to filter your array :
func getArticlesforCategory(category: String) -> Int {
let filteredArray = allArticles.filter( {$0.category == category }) // Here you have filtered array
return filteredArray.count // If you want number of items pass count of filtered array
}
try below line:
let result = self.allArticles.filter { $0.category == "test" }.count
You can use filter, try this code
func getFilteredArray(category: String) -> [ array of your objects] {
let filteredArray = allArticles.filter( {$0.category == category })
return filteredArray
}
i have myrecord class ..i need to find the index of object from tuple array
var transactionsGroupedByDate = [(String,Array<MyCushyRecords>)]()
where my object is myrecord object.
var transactionsGroupedByDate = [(String,Array<MyCushyRecords>)]()
i just changed Array of MyCushyRecords class to [Int] to finding index of tuples cause i don't know what exact your model does something like below
var transactionsGroupedByDate = [(String,[Int])]()
Now you can get index value like below,
var index:Int? = nil
for i in 0..<transactionsGroupedByDate.count {
let result = zip(transactionsGroupedByDate[i].1, YOUR_VALUE).enumerated().filter() {
$1.0 == $1.1
}.map{$0.0}
// result gives matches index value from both Int arrays.
if result.count == YOUR_VALUE.count {
index = i
break
}
}
//here you can check index of specific value in tuples
if index != nil {
print(index ?? "failed")
}else {
print("No matched values")
}
I have a dictionary that's updated from another class. I have a property observer on the dictionary so I know when a value has been added or removed.
I create a sorted array based on the values of the dictionary. I need to keep this array updated and retain the index associated with the update for use with a UITableView. My UI is as such that a wholesale reloading of data isn't possible - I need to directly insert or remove rows based on what the update was.
I have simplified this into a playground:
func dictionaryUpdated() {
print("dictionary updated")
// Add or remove string at index depending on order.
}
var myDictionary : [Int:String] = ["Bob".hashValue:"Bob","Dave".hashValue:"Dave","Yoda".hashValue:"Yoda","Windu".hashValue:"Windu","Obi Wan".hashValue:"Obi Wan","Qui-gon".hashValue:"Qui-gon","Anakin".hashValue:"Anakin"] { didSet { dictionaryUpdated() } }
func addEntry(entry: String) {
myDictionary[entry.hashValue] = entry
}
func removeEntry(entry: String) {
myDictionary.removeValueForKey(entry.hashValue)
}
// sort the keys alphabetically while creating the array
var valuesArray = myDictionary.values.sort { (lhs, rhs) -> Bool in
return lhs < rhs
}
I have tried using an NSMutableOrderedSet but the keys can only be Strings.
Just playing around in playground. Can be much more elegant though...
var valuesArray: [String] = [] { didSet { valuesArray.sortInPlace { $0 < $1 } } }
func dictionaryUpdated(old: [Int: String]) {
let added = myDictionary.count > old.count
let item: [String] = added ? myDictionary.values.filter { !old.values.contains($0) } : old.values.filter { !myDictionary.values.contains($0) }
valuesArray += item
let index = valuesArray.indexOf(item[0])!
print("item " + (added ? "added" : "removed") + ": \(item) at index \(index)")
}
var myDictionary: [Int: String] = ["Yoda".hashValue: "Yoda", "Windu".hashValue: "Windu", "Obi Wan".hashValue: "Obi Wan"] {
didSet {
dictionaryUpdated(oldValue)
}
}
addEntry("Darth Vader")
print(valuesArray)
Output:
item added: ["Darth Vader"] at index 0
["Darth Vader", "Obi Wan", "Windu", "Yoda"]
Assuming you have the sorted array before and after the property change (which can be achieved via another instance variable), what you need to do is to compare the old and the new array, and detect which which indexes changed.
An elegant solution to this problem would be to add a diff method to the Array class which computes the difference. The method might look something like this:
extension Array where Element: Equatable {
func diff(other: [Element]) -> (added: [Int], deleted: [Int], moved: [(from: Int, to: Int)]) {
var added: [Int] = []
var deleted: [Int] = []
var moved: [(from: Int, to: Int)] = []
for (i, item) in enumerate() {
if let j = other.indexOf({$0 == item}) {
if i != j {
moved.append((from: i, to: j))
}
} else {
deleted.append(i)
}
}
for (i, item) in other.enumerate() {
if indexOf({$0 == item}) == nil {
added.append(i)
}
}
return (added: added, deleted: deleted, moved: moved)
}
}
You would then use like this: valuesArray.diff(oldValuesArray).
Any idea how can i shuffle randomly for RLMResults? I couldn't find anyway to do it via RLMSortDescriptor.
I tried writing my own swap algorithm, but it's giving me error (cannot assign to results of this expression*).
func simpleRandomSwap(rlmobjects:RLMResults!) -> RLMResults! {
if (rlmobjects != nil && rlmobjects.count > 0) {
let indexcount = Int(rlmobjects.count)
for (var i=0 ; i<indexcount; i++) {
let randomUInt:UInt = UInt(arc4random_uniform(UInt32(indexcount)))
let myuint = UInt(i)
// simple swap
var temp = rlmobjects.objectAtIndex(myuint)
rlmobjects.objectAtIndex(randomUInt) = rlmobjects.objectAtIndex(myuint)
rlmobjects.objectAtIndex(myuint) = temp
} // end for loop
}
return rlmobjects
}
First a quick note: RLMResults have no defined order unless they are sorted via RLMSortDescriptor.
Instead, to randomly sort you have two options:
Add a integer property to your object schema and assign random values to all the objects. You can then use a RLMSortDescriptor with RLMResults to sort the objects.
As mentioned in the comments above, copy the RLMObjects into an array and perform a random sort on this.
Here is an extension to RLMObject that does this:
extension RLMResults {
public func toShuffledArray<T>(ofType: T.Type) -> [T] {
var array = [T]()
for result in self {
if let result = result as? T {
array.append(result)
}
}
let count = array.count
if count > 1 {
for i in 0..<(count - 1) {
let j = Int(arc4random_uniform(UInt32(count - i))) + Int(i)
swap(&array[i], &array[j])
}
}
return array
}
}
You would call it like this:
let shuffled = results.toShuffledArray(TestObject.self)
Swift 5:
You can just use default method:
let shuffled = results.shuffled()