I have an array of string arrays.
it has to be declared empty, and it's later filled one array at a time.
I have tried any possible data structure, but none of them seems to be working.
For example, if I declare
var array = [[String]]()
the app crashes when I try to append elements, like
array[1][0] = "Some string"
The only way I managed to make it work is by declaring the array with a fixed number of elements, but that's not good for my purpose.
I thought this was very easy to accomplish, but I encountered a lot of problems, any solution?
You cannot append to empty array like this array[1][0] = "Some string". If you want to do that, you need to create an array with certain size and repeated value. But you want to create 2d-array, so that can be implemented like this:
extension Array {
static func bidimensional<T>(row: Int, _ column: Int, repeatedValue: T) -> [[T]] {
var resultArray = [[T]]()
for _ in 0...column {
resultArray.append([T](count: row, repeatedValue: repeatedValue))
}
return resultArray
}
}
var arr = [String].bidimensional(2, 2, repeatedValue: "")
arr[1][0] = "Hello there!"
print(arr[1][0]) // "Hello there!"
Update
Extension add new functionality to an existing class, structure, or enumeration type. In our case we extend Array type with function bidimensional. The static means, that it's type method, not instance method. The T means, that it's generic method. In this case, you can call your bidimensional function not only with one specific type (like String), but any type you want (String, Int, Double, etc.). Well, the bidimensional's func code is pretty simple. We just create empty 2D resultArray with our custom (T) type. Then we fill this array with our repeatedValue and return it. Thats all.
P.S. To be clear with generics there are several examples:
[Int].bidimensional(2, 2, repeatedValue: 0) // 2x2 with repeatedValue 0
[Double].bidimensional(5, 1, repeatedValue: 2.1) // 5x1 with repeatedValue 2.1
------
struct MyCustomStruct {
var variable: String
}
var myCustomStruct = MyCustomStruct(variable: "repeat")
var arr = [MyCustomStruct].bidimensional(2, 2, repeatedValue: myCustomStruct)
print(arr[0][1].variable) // "repeat"
Update 2 for this comment.
class MyClass {
var arr = [[String]]()
func fill2DArrayInLoop() {
arr = [String].bidimensional(2, 2, repeatedValue: "")
for i in 0...1 {
for j in 0...1 {
arr[i][j] = "row: \(i); col: \(j)"
}
}
}
}
let myClass = MyClass()
myClass.fill2DArrayInLoop()
print(myClass.arr[0][1]) // "row: 0, col: 1"
Found the way to do this better (see this answer):
class MyClass {
var arr = Array(count: 2, repeatedValue: Array(count: 2, repeatedValue: ""))
func fill2DArrayInLoop() {
for i in 0...1 {
for j in 0...1 {
arr[i][j] = "row: \(i); col: \(j)"
}
}
}
}
let myClass = MyClass()
myClass.fill2DArrayInLoop()
print(myClass.arr[0][1]) // "row: 0, col: 1"
Now you don't need to create extension. Just put another declaration of array to repeatedValue: parameter.
Related
I have 2 sections in my tableview. Section 0 has data from an array and section 1 has data from an api. Now I want to check if the data from section 0 has a matching data to that of section 1 then I want to remove that particular entry from section 1. Not sure how to do that…
I have 2 arrays which populate data in each of the sections:
filteredSelectedProductList —> Section 0
productsList —> Section 1
This is the code I have:
if self.filteredSelectedProductList.isEmpty == false {
for prod in self.filteredSelectedProductList { //section 0
for filtProd in productsList { //section 1
if prod.productId == filtProd.productId {
//Here I want to remove the matching entry from productsList array. How can I do that..?
}
}
}
}
You can use filter where not contains,
something like this should work,
var arr1 = [1,3,4,5,6] // array 1 section 0
var arr2 = [2,34,5,6] // array 2 section 1
print(arr2.filter{!arr1.contains($0)}) // [2,34]
In your case you are using custom model, you can confirm to Equatable and do the following with it, then you can simple use it as i showed you above.
struct MyCustomObject {
var id: Int // some unique id you can compare things to
}
extension MyCustomObject: Equatable {
static func == (lhs: MyCustomObject, rhs: MyCustomObject) -> Bool {
return lhs.id == rhs.id
}
}
Usage :
var arra1: [MyCustomObject] = [] // section 0
var arra2: [MyCustomObject] = [] // section 1
print(arra2.filter{!arra1.contains($0)})
Swift 4.1
You can do by this way, You just have to put your array instead of this
var array1 = ["45","34","67"] //section 0
var array2 = ["45", "23"] // section 1
array2.removeAll(where: { array1.contains($0) }) // finalArray = ["23"]
You can use removeAll:
productsList.removeAll(where: { filteredSelectedProductList.contains($0) })
To use the contains, your models must conform to Equatable, otherwise you should do like this:
productsList.removeAll(where: { item in filteredSelectedProductList.contains(where: { $0.productId == item.productId }) })
Array.contains is an O(n) operation. Meaning that each element in the first array, you may have to search all of the second array. Resulting in O(m * n), m and n being the counts of the arrays, which is not efficient.
A better way would be to:
build a dictionary of the occurrences of elements in the first array,
then loop through the second array,
Append the elements in the second array that don't belong to the first one.
Here a function that does the above steps with a time complexity of O(m + n):
func intersect<T: Hashable>(_ a1: [T], with a2: [T]) -> [T] {
var result = [T]()
result.reserveCapacity(a2.count)
var dict1: [T: Int] = [T: Int].init(minimumCapacity: a1.count)
for x in a1 {
dict1[x, default: 0] += 1
}
for y in a2 {
if dict1[y] == nil || dict1[y] == 0 {
result.append(y)
} else {
dict1[y]! -= 1
}
}
return result
}
Note that the elements of the arrays have to conform to Hashable in order to use a dictionary. If an element occurs in the second array more than in the first, the extra duplicates are the ones kept. Which is not handled in all other answers
Here are some use cases :
let array1 = [1, 2, 3, 4, 5, 6]
let array2 = [1, 2, 2, 4, 7, 6]
intersect(array1, with: array2) //[2, 7]
struct MyStruct: Hashable { var id: Int }
let array3 = [MyStruct(id: 0), MyStruct(id: 2), MyStruct(id: 3)]
let array4 = [MyStruct(id: 1), MyStruct(id: 2), MyStruct(id: 2)]
intersect(array3, with: array4) //[{id 1}, {id 2}]
I have 2 Arrays. Say, array1 = [1,2,3,4,5] and array2 = [2,3]. How could I check in swift if array1 contains at least one item from array2?
You can do this by simply passing in your array2's contains function into your array1's contains function (or vice versa), as your elements are Equatable.
let array1 = [2, 3, 4, 5]
let array2 = [20, 15, 2, 7]
// this is just shorthand for array1.contains(where: { array2.contains($0) })
if array1.contains(where: array2.contains) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works by looping through array 1's elements. For each element, it will then loop through array 2 to check if it exists in that array. If it finds that element, it will break and return true – else false.
This works because there are actually two flavours of contains. One takes a closure in order to check each element against a custom predicate, and the other just compares an element directly. In this example, array1 is using the closure version, and array2 is using the element version. And that is the reason you can pass a contains function into another contains function.
Although, as correctly pointed out by #AMomchilov, the above algorithm is O(n2). A good set intersection algorithm is O(n), as element lookup is O(1). Therefore if your code is performance critical, you should definitely use sets to do this (if your elements are Hashable), as shown by #simpleBob.
Although if you want to take advantage of the early exit that contains gives you, you'll want to do something like this:
extension Sequence where Iterator.Element : Hashable {
func intersects<S : Sequence>(with sequence: S) -> Bool
where S.Iterator.Element == Iterator.Element
{
let sequenceSet = Set(sequence)
return self.contains(where: sequenceSet.contains)
}
}
if array1.intersects(with: array2) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works much the same as the using the array's contains method – with the significant difference of the fact that the arraySet.contains method is now O(1). Therefore the entire method will now run at O(n) (where n is the greater length of the two sequences), with the possibility of exiting early.
With Swift 5, you can use one of the following paths in order to find if two arrays have common elements or not.
#1. Using Set isDisjoint(with:) method
Set has a method called isDisjoint(with:). isDisjoint(with:) has the following declaration:
func isDisjoint(with other: Set<Element>) -> Bool
Returns a Boolean value that indicates whether the set has no members in common with the given sequence.
In order to test if two arrays have no common elements, you can use the Playground sample code below that implements isDisjoint(with:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [50, 100, 200]
let hasNoCommonElement = Set(array1).isDisjoint(with: array2)
print(hasNoCommonElement) // prints: true
#2. Using Set intersection(_:) method
Set has a method called intersection(_:). intersection(_:) has the following declaration:
func intersection<S>(_ other: S) -> Set<Element> where Element == S.Element, S : Sequence
Returns a new set with the elements that are common to both this set and the given sequence.
In order to test if two arrays have no common elements or one or more common elements, you can use the Playground sample code below that implements intersection(_:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [2, 3, 18]
let intersection = Set(array1).intersection(array2)
print(intersection) // prints: [18, 3]
let hasCommonElement = !intersection.isEmpty
print(hasCommonElement) // prints: true
An alternative way would be using Sets:
let array1 = [1,2,3,4,5]
let array2 = [2,3]
let set1 = Set(array1)
let intersect = set1.intersect(array2)
if !intersect.isEmpty {
// do something with the intersecting elements
}
Swift 5
Just make an extension
public extension Sequence where Element: Equatable {
func contains(anyOf sequence: [Element]) -> Bool {
return self.filter { sequence.contains($0) }.count > 0
}
}
Use:
let someArray = ["one", "two", "three"]
let string = "onE, Cat, dog"
let intersects = string
.lowercased()
.replacingOccurrences(of: " ", with: "")
.components(separatedBy: ",")
.contains(anyOf: someArray)
print(intersects) // true
let a1 = [1, 2, 3]
let a2 = [2, 3, 4]
Option 1
a2.filter { a1.contains($0) }.count > 1
Option 2
a2.reduce(false, combine: { $0 || a1.contains($1) })
Hope this helps.
//
// Array+CommonElements.swift
//
import Foundation
public extension Array where Element: Hashable {
func set() -> Set<Array.Element> {
return Set(self)
}
func isSubset(of array: Array) -> Bool {
self.set().isSubset(of: array.set())
}
func isSuperset(of array: Array) -> Bool {
self.set().isSuperset(of: array.set())
}
func commonElements(between array: Array) -> Array {
let intersection = self.set().intersection(array.set())
return intersection.map({ $0 })
}
func hasCommonElements(with array: Array) -> Bool {
return self.commonElements(between: array).count >= 1 ? true : false
}
}
I have this class:
class MainView:UIView{
var categories:[Category]!
}
i want to set the categories arg, but i need to pass it by reference not value. because it's more efficient and better.
so if i did this:
let mainView = MainView()
mainView.categories = categoriesData.
then it pass it by value.
if i need to pass it by reference i could do that by using function inside the MainView()
class MainView:UIView{
var categories:[Category]!
fun setCategories(inout categories: Int){
self.categories = categories;
}
}
but if i don't want to use set function, How could i pass it by reference.
e.g
mainView.categories = &categoriesData. but that doesn't work ?thanks
Swift uses ARC (Automatic Reference Counting) when dealing with arrays, and it delays copying arrays until one of the copies is modified:
For example:
var a = [1, 2, 3, 4, 5]
let b = a
let c = a // 1
a.append(6) // 2
print(a.count)
print(b.count)
print(c.count)
At step 1 above, there is only one copy of [1, 2, 3, 4, 5] in memory, and a, b, and c are references to it.
When a is modified in step 2, Swift gives a and new copy of the array and b and c continue to reference the original array. So now there are 2 copies of the array in memory.
Let's look at a much more involved example:
class Person: CustomStringConvertible {
let name: String
var friends: [Person] = []
init(name: String) {
self.name = name
}
var description: String { return name }
}
func createFredsFriends() -> [Person] {
let barney = Person(name: "Barney")
let wilma = Person(name: "Wilma")
let betty = Person(name: "Betty")
let friends = [barney, wilma, betty] // 1
return friends
}
func createFred() -> Person {
let fred = Person(name: "Fred")
let friends = createFredsFriends() // 2
fred.friends = friends // 3
return fred
}
let fred = createFred() // 4
print(fred.friends) // [Barney, Wilma, Betty]
At step 1, the array of friends is created. It is referenced by the local variable friends.
This reference goes away when createFredsFriends() returns, and the only reference to the array is held then by the local variable friends at step 2. Ownership of the array has been passed.
At step 3, a second reference to the array has been assigned to the friends property of fred.
At step 4, createFred() has returned, so the local variable friends is gone and no longer references the array. The only reference is in the property of the fred object which is held by the variable fred.
So the array was created once, several references to it were created, and in the end there is a single reference to the array and all of this was done without a single copy operation.
Since Swift arrays are value types and copied when changed, you can't pass an array and then expect the original to be updated when the copy is. If you need that level of functionality, you can create a class wrapper for the array and then always access that array through the instance of that class.
Here I've modified the previous example to show how that would work:
// Class to wrap array so that everyone references the same copy
class FriendsWrapper {
var friends: [Person]
init(friends: [Person]) {
self.friends = friends
}
}
class Person: CustomStringConvertible {
let name: String
var friendsWrapper: FriendsWrapper?
init(name: String) {
self.name = name
}
func addFriend(friend: Person) {
if let wrapper = friendsWrapper {
wrapper.friends.append(friend)
} else {
friendsWrapper = FriendsWrapper(friends: [friend])
}
}
var description: String { return name }
}
func createFredsFriends() -> [Person] {
let barney = Person(name: "Barney")
let wilma = Person(name: "Wilma")
let betty = Person(name: "Betty")
let friends = [barney, wilma, betty]
return friends
}
func createFred() -> Person {
let fred = Person(name: "Fred")
let friendsWrapper = FriendsWrapper(friends: createFredsFriends())
fred.friendsWrapper = friendsWrapper
// Add friend to Fred object
fred.addFriend(Person(name: "Bam Bam"))
// Copy of array in local friendsWrapper is updated
print(friendsWrapper.friends) // [Barney, Wilma, Betty, Bam Bam]
return fred
}
let fred = createFred()
is there a possibility to get an object from an array with an specific property? Or do i need to loop trough all objects in my array and check if an property is the specific i was looking for?
edit: Thanks for given me into the correct direction, but i have a problem to convert this.
// edit again: A ok, and if there is only one specific result? Is this also a possible method do to that?
let imageUUID = sender.imageUUID
let questionImageObjects = self.formImages[currentSelectedQuestion.qIndex] as [Images]!
// this is working
//var imageObject:Images!
/*
for (index, image) in enumerate(questionImageObjects) {
if(image.imageUUID == imageUUID) {
imageObject = image
}
}
*/
// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
var imageObject = questionImageObjects.filter( { return $0.imageUUID == imageUUID } )
// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
You have no way to prove at compile-time that there is only one possible result on an array. What you're actually asking for is the first matching result. The easiest (though not the fastest) is to just take the first element of the result of filter:
let imageObject = questionImageObjects.filter{ $0.imageUUID == imageUUID }.first
imageObject will now be an optional of course, since it's possible that nothing matches.
If searching the whole array is time consuming, of course you can easily create a firstMatching function that will return the (optional) first element matching the closure, but for short arrays this is fine and simple.
As charles notes, in Swift 3 this is built in:
questionImageObjects.first(where: { $0.imageUUID == imageUUID })
Edit 2016-05-05: Swift 3 will include first(where:).
In Swift 2, you can use indexOf to find the index of the first array element that matches a predicate.
let index = questionImageObjects.indexOf({$0.imageUUID == imageUUID})
This is bit faster compared to filter since it will stop after the first match. (Alternatively, you could use a lazy sequence.)
However, it's a bit annoying that you can only get the index and not the object itself. I use the following extension for convenience:
extension CollectionType {
func find(#noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
return try indexOf(predicate).map({self[$0]})
}
}
Then the following works:
questionImageObjects.find({$0.imageUUID == imageUUID})
Yes, you can use the filter method which takes a closure where you can set your logical expression.
Example:
struct User {
var firstName: String?
var lastName: String?
}
let users = [User(firstName: "John", lastName: "Doe"), User(firstName: "Bill", lastName: "Clinton"), User(firstName: "John", lastName: "Travolta")];
let johns = users.filter( { return $0.firstName == "John" } )
Note that filter returns an array containing all items satisfying the logical expression.
More info in the Library Reference
Here is a working example in Swift 5
class Point{
var x:Int
var y:Int
init(x:Int, y:Int){
self.x = x
self.y = y
}
}
var p1 = Point(x:1, y:2)
var p2 = Point(x:2, y:3)
var p3 = Point(x:1, y:4)
var points = [p1, p2, p3]
// Find the first object with given property
// In this case, firstMatchingPoint becomes p1
let firstMatchingPoint = points.first{$0.x == 1}
// Find all objects with given property
// In this case, allMatchingPoints becomes [p1, p3]
let allMatchingPoints = points.filter{$0.x == 1}
Reference:
Trailing Closure
Here is other way to fetch particular object by using object property to search an object in array.
if arrayTicketsListing.contains({ $0.status_id == "2" }) {
let ticketStatusObj: TicketsStatusList = arrayTicketsListing[arrayTicketsListing.indexOf({ $0.status_id == "2" })!]
print(ticketStatusObj.status_name)
}
Whereas, my arrayTicketsListing is [TicketsStatusList] contains objects of TicketsStatusList class.
// TicketsStatusList class
class TicketsStatusList {
internal var status_id: String
internal var status_name: String
init(){
status_id = ""
status_name = ""
}
}
var sourceEntries: [Entry] = [entry1, ..., entry14]
var myDict: Dictionary<String, [Entry]> = [:]
for entry in sourceEntries {
if var array = myDict[entry.attribute1] { theArray.append(entry) }
else { myDict[entry.attribute1] = [entry] }
}
I am intending to create a Dictionary, which matches all the objects of the struct "Eintrag" with the same attribute from the source-Array "alleEinträge" to a String containing the value of the shared attribute. For some reason my final Dictionary just matches Arrays of one element to the Strings, although some Arrays ought to contain up to four elements.
The problem is that the array is passed by value (i.e. "copied"), so the array you are writing to when you say array.append is not the array that is "inside" the dictionary. You have to write back into the dictionary explicitly if you want to change what's in it.
Try it in a simple situation:
var dict = ["entry":[0,1,2]]
// your code
if var array = dict["entry"] { array.append(4) }
// so what happened?
println(dict) // [entry: [0, 1, 2]]
As you can see, the "4" never got into the dictionary.
You have to write back into the dictionary explicitly:
if var array = dict["entry"] { array.append(4); dict["entry"] = array }
FURTHER THOUGHTS: You got me thinking about whether there might be a more elegant way to do what you're trying to do. I'm not sure whether you will think this is "more elegant", but perhaps it has some appeal.
I will start by setting up a struct (like your Entry) with a name attribute:
struct Thing : Printable {
var name : String
var age : Int
var description : String {
return "{\(self.name), \(self.age)}"
}
}
Now I will create an array like your sourceEntries array, where some of the structs share the same name (like your shared attribute attribute1):
let t1 = Thing(name: "Jack", age: 40)
let t2 = Thing(name: "Jill", age: 38)
let t3 = Thing(name: "Jill", age: 37)
let arr = [t1,t2,t3]
And of course I will prepare the empty dictionary, like your myDict, which I call d:
var d = [String : [Thing]]()
Now I will create the dictionary! The idea is to use map and filter together to do all the work of creating key-value pairs, and then we just build the dictionary from those pairs:
let pairs : [(String, [Thing])] = arr.map {
t in (t.name, arr.filter{$0.name == t.name})
}
for pair in pairs { d[pair.0] = pair.1 }