Find element in an array of object - arrays

I created an array of objects:
var fullMonthlyList = [SimulationMonthly]()
The class here:
class SimulationMonthly {
var monthlyMonthDuration: NSNumber = 0
var monthlyYearDuration: NSNumber = 0
var monthlyFullAmount: NSNumber = 0
var monthlyAmount: Int = 0
init(monthlyMonthDuration: NSNumber, monthlyYearDuration: NSNumber, monthlyFullAmount: NSNumber, monthlyAmount: Int){
self.monthlyMonthDuration = monthlyMonthDuration
self.monthlyYearDuration = monthlyYearDuration
self.monthlyFullAmount = monthlyFullAmount
self.monthlyAmount = monthlyAmount
}
}
I just did append to populate it, now I want to find for example if they're an existing value, for example monthlyAmount equals to "194" by search in the array, how can I do ? I have tried filter and contains but I get errors.
What I've tried:
if self.fullMonthlyList.filter({ $0.monthlyAmount == self.monthlyAmount.intValue }) { ... }
Error:
Cannot invoke 'filter' with an argument list of type '((SimulationMonthly) throws -> Bool)'

You can do:
if let sim = fullMonthlyList.first(where: { $0.monthlyAmount == 194 }) {
// Do something with sim or print that the object exists...
}
This will give you the first element in your array where monthlyAmount equals 194.
If you want all elements with that condition, you can use filter:
let result = fullMonthlyList.filter { $0.monthlyAmount == 194 }
If you don't need the object at all but you just want to know if one exists, then contains would be enough:
let result = fullMonthlyList.contains(where: { $0.monthlyAmount == 194 })

Here's a simple playground example of filtering objects based on matching a property. You should be able to expand it to your situation.
class Item {
var value: Int
init(_ val: Int) {
value = val
}
}
var items = [Item]()
for setting in 0..<5 {
items.append(Item(setting))
}
func select(_ with: Int) -> [Item] {
return items.filter { $0.value == with }
}
let found = select(3)

Related

How can I merge 2 dictionaries into one array?

My JSON data look like this image below. Now I wanna merge the value of Shop Type and Promotion into one to use as collection view data. How can I do that?
I just filter the response data from the server like this:
var dataBanDau: [SDFilterModel] = []
var quickData: [SDFilterModel] = []
let filters: [SDFilterModel] = data
self.filterEntries = filters
//let nsarray = NSArray(array: self.filterEntries! , copyItems: true)
// self.filterEntriesStoreConstant = nsarray as! Array
self.dataBanDau = filters
for i in 0..<self.dataBanDau.count {
if self.dataBanDau[i].search_key.count == 0 {
self.quickData.append(self.dataBanDau[i])
}
}
self.quickData = self.quickData.filter {
$0.type != "range"
}
DispatchQueue.main.async {
//Note: Reload TableView
self.quickFilterCollection.reloadData()
completed(true)
}
}
the class SDFilterModel:
class SDFilterModel: DSBaseModel {
var name = String()
var type = String()
var is_expanded = Int()
var search_key = String()
var filterEntries : [SDFilterModel]?
override func copy(with zone: NSZone? = nil) -> Any {
// This is the reason why `init(_ model: GameModel)`
// must be required, because `GameModel` is not `final`.
let copy = SDFilterModel(dict: self.dictionary)
if let arrAttribute = NSArray(array: self.value , copyItems: true) as? [AttributeValueModel] {
copy.value = arrAttribute
}
return copy
}
override init(dict: Dictionary<String, Any>) {
super.init(dict: dict);
value = self.valueParse()
name = dict.getString(forKey: "name")
type = dict.getString(forKey: "type")
search_key = dict.getString(forKey: "search_key")
is_expanded = dict.getInt(forKey: "is_expanded")!
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
var value: [AttributeValueModel] = [];
func valueParse()-> [AttributeValueModel] {
guard let childs = (self.dictionary["value"]) as? [Dictionary<String, AnyObject>]
else { return [] }
var output: [AttributeValueModel] = [];
for aDict in childs {
let item = AttributeValueModel(dict:aDict);
// if type == .Range && item.option_id == "0" {
// item.setRangeOptionID(aValue: item.option_name!)
// }
//
output.append(item);
}
return output;
}
Let be Assume you have let myArray = [1,2,3,4,5,6,7,8]
Now you wanted to square of each and every element in the array,
With for loop you do like this
for item in myArray {
print(item * item)
}
Now assume item = $0
With for map you jus do
myArray.map({ $0 * $0 })
Both will gave same output.
map : Use to do same operation on every element of array.
flatmap : It is used to flattern the array of array.
let myArr = [[1,2,3],[4,5,6,7]]
and you want o/p as [1,2,3,4,5,6,7]
So can get above output with myArr.flatMap({$0})
Now back to your question.
let reqArray = myModel.data.map({ $0.value }).flatMap({ $0 })
First, map gaves you array-of-array of key value but you need a single array, so for that you need to use flatmap.
You can take ref : https://medium.com/#Dougly/higher-order-functions-in-swift-sorted-map-filter-reduce-dff60b5b6adf
Create the models like this
struct Option {
let name: String
let searchKey: String
let id: String
}
struct Model {
let type: String
let name: String
let isExpanded: Bool
let value: [Option]
}
You should get the options array values and join all the arrays
let models:[Model] = //...
let collectionViewArray = models.map { $0.value }.reduce([Option](), +)
Using for loop
var collectionViewArray = [Option]()
for model in models {
collectionViewArray.append(contentsOf: model.value)
}

Sum of Array containing custom class (Swift)

i am figuring out a way to get the sum of values of classes which are contained in an array. My setup is as follows:
class CustomClass {
var value: Int?
init(value: Int) {
self.value = value
}
}
let object1 = CustomClass(value: 2)
let object2 = CustomClass(value: 4)
let object3 = CustomClass(value: 8)
let array: [CustomClass] = [object1, object2, object3]
My current solution is as follows:
var sumArray = [Int]()
for object in array {
sumArray.append(object.value!)
}
let sum = sumArray.reduce(0, +)
The problem is that it gets very complex with classes with many other values, does anybody know a better solution?
You can compactMap your array of custom class into array of integer and then reduce that array to its sum. Like,
let sum = array.lazy.compactMap { $0.value }
.reduce(0, +)
You can use a single reduce on array.
let sumOfValues = array.reduce({$0 += ($1.value ?? 0)})
I would create a protocol for your class or structures that contains a value. And change its declaration to non optional.
protocol Valueable {
var value: Int { get }
}
Then you will need to make your class conform to that protocol:
class CustomClass: Valueable {
let value: Int
init(value: Int) {
self.value = value
}
}
Now you can extend the collection protocol with a read only instance property to return the sum of all elements in your array.
extension Collection where Element: Valueable {
var sum: Int {
return reduce(0) { $0 + $1.value }
}
}
let object1 = CustomClass(value: 2)
let object2 = CustomClass(value: 4)
let object3 = CustomClass(value: 8)
let objects = [object1, object2, object3]
let sum = objects.sum // 14
edit/update:
Another option is to extend sequence and add a generic sum method that accepts a key path that its property conforms to AdditiveArithmetic
or add an associated type to the protocol that conforms to AdditiveArithmetic:
protocol Valueable {
associatedtype Value: AdditiveArithmetic
var value: Value { get }
}
extension Collection where Element: Valueable {
var sum: Element.Value { reduce(.zero) { $0 + $1.value } }
}
class CustomClass: Valueable {
let value: Decimal
init(value: Decimal) {
self.value = value
}
}
Usage:
let object1 = CustomClass(value: 123.4567)
let object2 = CustomClass(value: 12.34567)
let object3 = CustomClass(value: 1.234567)
let objects = [object1, object2, object3]
let sum = objects.sum // 137.036937
Short way:
let sum = customArray.reduce(0) { $0 + $1.intValue }

Counting unique ids within nested array using Swift

I have the following nested array and I'm trying to count how many items with unique ids there are. In the array below the count should be 2.
Array is of type List<SolData> it comes from Realm
class SolData: Object {
#objc dynamic var uid = "";
#objc dynamic var id = "";
}
extension SolData: ListDiffable {
func diffIdentifier() -> NSObjectProtocol {
return uid as NSObjectProtocol
}
func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
if let object = object as? SolData {
return uid == object.uid
}
return false
}
}
Print of the array.
(
[0] SolData {
uid = sdasd;
id = jmX3;
},
[1] SolData {
uid = gfd;
id = jmX3;
},
[2] SolData {
uid = hgfd;
id = jmX3;
},
[3] SolData {
uid = terw;
id = jmX3;
},
[4] SolData {
uid = fgg;
id = GFdda;
}
)
I tried to use map in the following way
var count = 0;
var prevId = "";
let uniqueSols = diff.sol.map{ (s) -> Int in
if s.id != prevId {
count = count + 1;
prevId = s.id;
}
return count;
}
print(uniqueSols);
But I get the following error.
SWIFT RUNTIME BUG: unable to demangle type of field '_transform'. mangled type name is 'q_7ElementSTQzc'
2018-10-27 14:26:08.793528+0300 App[23634:611928] SWIFT RUNTIME BUG: unable to demangle type of field '_transform'. mangled type name is 'q_7ElementSTQzc', _transform: ())
To reproduce your code, I am going to mock the SolData class and add an initializer to make instantiation easier:
class SolData {
var uid: String = ""
var id: String = ""
init(uid: String, id: String) {
self.uid = uid
self.id = id
}
}
Let's create a few of instances:
let zero = SolData(uid: "sdasd", id: "jmX3")
let one = SolData(uid: "gfd", id: "jmX3")
let two = SolData(uid: "hgfd", id: "jmX3")
let three = SolData(uid: "terw", id: "jmX3")
let four = SolData(uid: "fgg", id: "GFdda")
And group them in an array:
let array = [zero, one, two, three, four]
To get only instances that have unique ids, let's use reduce(into:) :
let uniqueIds = array.reduce(into: Set<String>(), { $0.insert($1.id)})
The count property of uniqueIds is the number of unique ids in array:
let uniqueIdsCount = uniqueIds.count //2
If you want an array of instances with unique ids, use the following:
let instancesWithUniqueIds = array.reduce(into: [SolData]()) { accumulator, element in
if accumulator.allSatisfy({ $0.id != element.id}) {
accumulator.append(element)
}
}
accumulator.allSatisfy({ $0.id != element.id}) maybe replaced by accumulator.contains(element) and making SolData conform to Hashable.

How to append custom object to an array in Swift?

How to append custom class object to an array in Swift?
Below is my code, but it shows error.
Error:
"Cannot assign value of '()' to type [PhotoVC]"
Code:
var photoVCs = [PhotoVC]()
for index in 0 ..< photos.count {
if let vc = getPhotoController(index) {
photoVCs = photoVCs.append(vc)
}
}
func getPhotoController(index: Int) -> PhotoVC? {
if index < 0 || index == NSNotFound {
return nil
}
else if index == photos.count {
return nil
}
let PhotoVCID = "PhotoVCID"
if let storyboard = storyboard,
pageVC = storyboard.instantiateViewControllerWithIdentifier(PhotoVCID) as? PhotoVC {
pageVC.photoName = photos[index]
pageVC.photoIndex = index
return pageVC
}
return nil
}
I should be able to do it, but what's the problem?
append does not return anything. Remove the assignment:
photoVCs = photoVCs.append(vc) // wrong
photoVCs.append(vc) // ok

Changing The value of struct in an array

I want to store structs inside an array, access and change the values of the struct in a for loop.
struct testing {
var value:Int
}
var test1 = testing(value: 6 )
test1.value = 2
// this works with no issue
var test2 = testing(value: 12 )
var testings = [ test1, test2 ]
for test in testings{
test.value = 3
// here I get the error:"Can not assign to 'value' in 'test'"
}
If I change the struct to class it works. Can anyone tell me how I can change the value of the struct.
Besides what said by #MikeS, remember that structs are value types. So in the for loop:
for test in testings {
a copy of an array element is assigned to the test variable. Any change you make on it is restricted to the test variable, without doing any actual change to the array elements. It works for classes because they are reference types, hence the reference and not the value is copied to the test variable.
The proper way to do that is by using a for by index:
for index in 0..<testings.count {
testings[index].value = 15
}
in this case you are accessing (and modifying) the actual struct element and not a copy of it.
Well I am going to update my answer for swift 3 compatibility.
When you are programming many you need to change some values of objects that are inside a collection. In this example we have an array of struct and given a condition we need to change the value of a specific object. This is a very common thing in any development day.
Instead of using an index to determine which object has to be modified I prefer to use an if condition, which IMHO is more common.
import Foundation
struct MyStruct: CustomDebugStringConvertible {
var myValue:Int
var debugDescription: String {
return "struct is \(myValue)"
}
}
let struct1 = MyStruct(myValue: 1)
let struct2 = MyStruct(myValue: 2)
let structArray = [struct1, struct2]
let newStructArray = structArray.map({ (myStruct) -> MyStruct in
// You can check anything like:
if myStruct.myValue == 1 {
var modified = myStruct
modified.myValue = 400
return modified
} else {
return myStruct
}
})
debugPrint(newStructArray)
Notice all the lets, this way of development is safer.
The classes are reference types, it's not needed to make a copy in order to change a value, like it happens with structs. Using the same example with classes:
class MyClass: CustomDebugStringConvertible {
var myValue:Int
init(myValue: Int){
self.myValue = myValue
}
var debugDescription: String {
return "class is \(myValue)"
}
}
let class1 = MyClass(myValue: 1)
let class2 = MyClass(myValue: 2)
let classArray = [class1, class2]
let newClassArray = classArray.map({ (myClass) -> MyClass in
// You can check anything like:
if myClass.myValue == 1 {
myClass.myValue = 400
}
return myClass
})
debugPrint(newClassArray)
To simplify working with value types in arrays you could use following extension (Swift 3):
extension Array {
mutating func modifyForEach(_ body: (_ index: Index, _ element: inout Element) -> ()) {
for index in indices {
modifyElement(atIndex: index) { body(index, &$0) }
}
}
mutating func modifyElement(atIndex index: Index, _ modifyElement: (_ element: inout Element) -> ()) {
var element = self[index]
modifyElement(&element)
self[index] = element
}
}
Example usage:
testings.modifyElement(atIndex: 0) { $0.value = 99 }
testings.modifyForEach { $1.value *= 2 }
testings.modifyForEach { $1.value = $0 }
How to change Array of Structs
for every element:
itemsArray.indices.forEach { itemsArray[$0].someValue = newValue }
for specific element:
itemsArray.indices.filter { itemsArray[$0].propertyToCompare == true }
.forEach { itemsArray[$0].someValue = newValue }
You have enough of good answers. I'll just tackle the question from a more generic angle.
As another example to better understand value types and what it means they get copied:
struct Item {
var value:Int
}
func change (item: Item, with value: Int){
item.value = value // cannot assign to property: 'item' is a 'let' constant
}
That is because item is copied, when it comes in, it is immutable — as a convenience.
Had you made Item a class type then you were able to change its value.
var item2 = item1 // mutable COPY created
item2.value = 10
print(item2.value) // 10
print(item1.value) // 5
This is very tricky answer. I think, You should not do like this:
struct testing {
var value:Int
}
var test1 = testing(value: 6)
var test2 = testing(value: 12)
var ary = [UnsafeMutablePointer<testing>].convertFromArrayLiteral(&test1, &test2)
for p in ary {
p.memory.value = 3
}
if test1.value == test2.value {
println("value: \(test1.value)")
}
For Xcode 6.1, array initialization will be
var ary = [UnsafeMutablePointer<testing>](arrayLiteral: &test1, &test2)
It is possible to use the map function to get this effect - essentially creating a new array
itemsArray = itemsArray.map {
var card = $0
card.isDefault = aCard.token == token
return card
}
I ended up recreating a new array of struct see the example below.
func updateDefaultCreditCard(token: String) {
var updatedArray: [CreditCard] = []
for aCard in self.creditcards {
var card = aCard
card.isDefault = aCard.token == token
updatedArray.append(card)
}
self.creditcards = updatedArray
}
I tried Antonio's answer which seemed quite logical but to my surprise it does not work. Exploring this further I tried the following:
struct testing {
var value:Int
}
var test1 = testing(value: 6 )
var test2 = testing(value: 12 )
var testings = [ test1, test2 ]
var test1b = testings[0]
test1b.value = 13
// I would assume this is same as test1, but it is not test1.value is still 6
// even trying
testings[0].value = 23
// still the value of test1 did not change.
// so I think the only way is to change the whole of test1
test1 = test1b

Resources