How to remove a swift 2 array element is not at index?
protocol Food { //Еда
var name: String {get}
func taste() -> String
var pr: Int {get}
}
protocol Storable: Food {
var expired: Bool {get}
var daysToExpire: Int {get}
}
class Apple: Food { //Яблоко
var name = "Яблоко"
func taste() -> String {
return "Сладкое"
}
var pr = 0
}
class Grapes: Storable { //виноград
var name = "Виноград"
func taste() -> String {
return "Кислый"
}
var pr = 0
var expired = false
var daysToExpire = 5
}
class Banana: Storable { //Банан
var name = "Банан"
func taste() -> String {
return "Сладкий"
}
var pr = 0
var expired = false
var daysToExpire = 9
}
class Potato: Food { //картошка
var name = "Картошк"
func taste() -> String {
return "Сладкая"
}
var pr = 1
}
class Bow: Food { //Лук
var name = "Лук"
func taste() -> String {
return "Горький"
}
var pr = 1
}
class Carrot: Food { //морковь
var name = "Морковь"
func taste() -> String {
return "Сладкая"
}
var pr = 1
}
var apple = Apple()
var grapers = Grapes()
var banana = Banana()
var potato = Potato()
var bow = Bow()
var carrot = Carrot()
var basket: [Food] = [apple, grapers, banana, potato, bow, carrot]
func bite(oneBite: [Food]) {
for bites in basket {
print("Кусаем \(bites.name), ооо на вкус \(bites.taste())")
}
}
bite(basket)
var fridge = [Storable]()//пустой массив типа Storable холодильник
for prod in basket {
if let storableProduct = prod as? Storable where storableProduct.expired == false {
fridge.append(storableProduct)
}
}
var trash = [Storable]() //пустой массив типа Storable мусорка
for i in fridge {
if i.daysToExpire > 6 {
fridge.removeAtIndex(i)
}
}
First of all please post the code rather than screenshots. Nobody who is willing to help is also willing to retype the code for testing.
The index variable i in the loop is a Storable object, not an Int index, that's exactly what the error message says.
Using enumerate you get the index and the object
for (i, storable) in fridge.enumerate {
if storable.days ... {
fridge.removeAtIndex(i)
}
}
But be aware that you certainly will run into the next problem if you are removing items from the object you are enumerating just now.
Related
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)
}
I have a certain model in the form of a structure:
struct ContactsModel {
let name: String
let status: String
let number: String
let onlineStatus: Bool
}
And there is a method that parses the array of names and adds them to the new collection.
var contactsDictionary = [String: [String]]()
var contactNameSectionTitles = [String]()
var names = [String]()
var contactsArray = [ContactsModel(name: "Test", status: "Test", number: "+7 999 999 99 99", onlineStatus: true)]
func configurateDictionary() {
names = contactsArray.map {$0.name}
for value in names {
let nameKey = String(value.prefix(1))
if var namePrefix = contactsDictionary[nameKey] {
namePrefix.append(value)
contactsDictionary[nameKey] = namePrefix
} else {
contactsDictionary[nameKey] = [value]
}
}
contactNameSectionTitles = [String](contactsDictionary.keys)
contactNameSectionTitles = contactNameSectionTitles.sorted(by: { $0 < $1 })
}
How do I do so in the method so that it returns not a string, but a model? I want the collection to be type
var contactsDictionary = [String: [ContactsModel]]()
There is an API to group an array to a dictionary
var contactsDictionary = [String:[ContactsModel]]()
var contactNameSectionTitles = [String]()
func configurateDictionary() {
contactsDictionary = Dictionary(grouping: contactsArray, by: { $0.prefix(1).uppercased() })
contactNameSectionTitles = contactsDictionary.keys.sorted()
}
You need Dictionary grouping by , e.x to group the array by name do
let arr = [ContactsModel]
let res = Dictionary(grouping: arr, by: { $0.name }) // [String:[ContactsModel]]
https://developer.apple.com/documentation/swift/dictionary/3127163-init
This method will return a dictionary of [String: ContactsModel]
func configurateDictionary() -> [String: ContactsModel] {
let result = contactsArray.reduce( [String: ContactsModel](), { (d, e) -> [String: ContactsModel] in
var dict = d
dict[e.name] = e
return dict
})
return result
}
If you want to change the configurateDictionary method to return [String: [ContactsModel]], create a temporary dictionary inside the function and return it at the end of the function
class ViewController: UIViewController {
var contactsDictionary = [String: [ContactsModel]]()
var contactNameSectionTitles = [String]()
var names = [String]()
var contactsArray = [ContactsModel(name: "Test", status: "Test", number: "+7 999 999 99 99", onlineStatus: true)]
override func viewDidLoad() {
super.viewDidLoad()
contactsDictionary = configurateDictionary()
}
func configurateDictionary() -> [String: [ContactsModel]] {
names.removeAll()
contactNameSectionTitles.removeAll()
var temp = [String: [ContactsModel]]()
for contact in contactsArray {
names.append(contact.name)
let nameKey = String(contact.name.prefix(1))
temp[nameKey, default: []].append(contact)
}
contactNameSectionTitles = [String](contactsDictionary.keys).sorted()
return temp
}
}
How to filter duplicate category element into different array and count their amount?
This is the format, record is from the core data.
var record = [Record]()
[<Record:...; data: {accountbook = "MyBook";
amount = "10.50";
category = "A";
id = 1;
},<Record:...; data: {accountbook = "MyBook";
amount = "5.50";
category = "B";
id = 2;
},<Record:...; data: {accountbook = "MyBook";
amount = "4.50";
category = "B";
id = 3;
}]
What I want
var category = ["A", "B"] //success
var total = [10.50, 10.00]
This is what I do for finding the category, and it works but how to group the same category and sum the total?
var category =[String]()
for categoryObject in record{
if let categoryItem = categoryObject.category{
category.append(categoryItem)
}
}
//I tried this code to group the same category but fail.
let result = Set(record).map{ category in return record.filter{$0 == category} }
Another way is this. but how if I have A-Z category? it will have very long code..Is there any way can detect the same value and split it to different array so that I can sum it by category.
categoryFilter = record.filter { $0.category!.contains("A") }
First group your record object by category like this
extension Sequence {
func group<GroupingType: Hashable>(by key: (Iterator.Element) -> GroupingType) -> [[Iterator.Element]] {
var groups: [GroupingType: [Iterator.Element]] = [:]
var groupsOrder: [GroupingType] = []
forEach { element in
let key = key(element)
if case nil = groups[key]?.append(element) {
groups[key] = [element]
groupsOrder.append(key)
}
}
return groupsOrder.map { groups[$0]! }
}
}
Then you will get your distinct arrays based on category like this
var records : [Record] = []// your objects
let distinctRecords = records.group(by: {$0. category})
Now you can use reduce to calculate sum of values of that category
for items in distinctRecords{
let sum = items.reduce(0.0){$0.0 + $1. amount ?? 0.0}// assuming you have float values in your amount
print(items)// do whatever you want to do with your distinct array
print(" \(sum)")
}
#Wan Jern I have written a piece of code, you can try this one. Hopefully, it will work.
var category = [String]()
var totalArr = [CGFloat]()
for categoryObject in record{
if let categoryItem = categoryObject.category{
if !category.contains(categoryItem) {
category.append(categoryItem)
totalArr.append(categoryObject.amount)
} else {
let index = category.index(of: categoryItem)
let itemAtIndex = category[index]
let itemAtIndex = itemAtIndex + categoryObject.amount
totalArr.insert(itemAtIndex, at: index)
}
}
}
Do you have your record struct in a class model?
like my data model selected from sqlite:
//Data model
import Foundation
import UIKit
class scoreModel: NSObject {
var lessonName:String = String()
var lessonCode:String = String()
var creditPoint:Double = Double()
var totalStudentNumber:Int = Int()
var teacherName:String = String()
var semesterName:String = String()
var scoreValue:String = String()
var studentCount:Int = Int()
}
If the answer is yes, we can use pointer in array and repeat while loop to do this manually.
Like my code:
let mysql = ""
let dataArray = SQLiteManager.shareInstance.queryDB(sql:mysql)
var i = 0
while i<dataArray.count-1
{
var scoreArray = [Dictionary<String, Int>]()
var range = 0
var test = 0
test = i
//print("pointer i is'\(test)'")
while ((dataArray[test]as! scoreModel).lessonCode == (dataArray[test+range]as! scoreModel).lessonCode && (test+range)<dataArray.count-1)
{
let key = (dataArray[test+range]as! scoreModel).scoreValue
let value = (dataArray[test+range]as! scoreModel).studentCount
var dict: [String: Int] = [String: Int]()
dict[key] = value
scoreArray.append(dict)
//print("working pointer is'\(test+range)'")
range = range+1
}
//transfer array
let model:resultModel = resultModel()
model.lessonName = (dataArray[test]as! scoreModel).lessonName
model.lessonCode = (dataArray[test]as! scoreModel).lessonCode
model.creditPoint = (dataArray[test]as! scoreModel).creditPoint
model.semesterName = (dataArray[test]as! scoreModel).semesterName
model.teacherName = (dataArray[test]as! scoreModel).teacherName
model.totalStudentNumber = (dataArray[test]as! scoreModel).totalStudentNumber
model.scoreArray = scoreArray
resultArray.add(model)
i = i+range
//print("range is'\(range)'")
}
Make the Records as Hashable with "category" as the unique in stubs function
struct Record: Hashable {
var accountbook = ""
var category = ""
var amount = 0.0
// added from stubs of Hashable
var hashValue: Int { return category.hashValue }
static func ==(lhs: Record, rhs: Record) -> Bool {
return lhs.category == rhs.category
}
}
Then filter the unique categories
let categories = Set(record).map { $0.category }
print(categories) // ["B", "A"]
And make a sum of each category
let totals = categories.map { c in
record.filter { $0.category == c }.map{ $0.amount }.reduce(0, +)
}
print(totals) // get the sums as [10.0, 10.5]
I have a Swift playground file where I have a list of factories with child entities called engines; where a player can hold many engines
The relationship is thus;
Factory -> Engine <- Player
One factory has many engines
One player has many engines
One engine has a single parent (factory)
One engine has a single owner (player)
I'm trying to build a Swift map and filter where, it produces a list of all factories that are not owned by a given player object.
My code now follows;
//: Swift Playground code
import Foundation
class Factory : CustomStringConvertible {
var name: String = ""
var engines: [Engine] = [Engine]()
var description: String {
return self.name
}
var owners: [Player]? {
let filtered = self.engines.filter { (eng:Engine) -> Bool in
return (eng.owner != nil)
}
.sorted { (engA:Engine, engB:Engine) -> Bool in
return ((engA.owner?.turnOrder)! < (engB.owner?.turnOrder)!)
}.flatMap { (eng:Engine) -> Player? in
return (eng.owner)
}
return filtered
}
init(name: String) {
self.name = name
// create 3 children (engines)
for _ in 1...3 {
let engine = Engine.init(parent: self)
self.engines.append(engine)
}
}
}
class Engine : CustomStringConvertible {
weak var parent: Factory?
weak var owner: Player?
var description: String {
guard let hasParent = self.parent else {
return "N/A"
}
return ("\(hasParent.name) - engine")
}
init(parent: Factory) {
self.parent = parent
}
func setOwner(owner: Player) {
self.owner = owner
self.owner?.addEngine(engine: self)
}
}
class Player : CustomStringConvertible {
var name: String = ""
var engines: [Engine] = [Engine]()
var turnOrder: Int = 0
var description: String {
return self.name
}
init(name: String) {
self.name = name
}
func addEngine(engine: Engine) {
self.engines.append(engine)
}
}
// create 3 factories
let f1 = Factory.init(name: "f1")
let f2 = Factory.init(name: "f2")
let f3 = Factory.init(name: "f3")
let factories = [f1,f2,f3]
let p1 = Player.init(name: "Bob")
if let firstEngine = f1.engines.first {
firstEngine.setOwner(owner: p1)
}
print ("All factories: \(factories)")
print ("p1 = \(p1.name), engines: \(p1.engines)")
for (index, f) in factories.enumerated() {
print ("#\(index), Owners: \(f.owners)")
}
// filter all factories NOT owned by player
let filtered = factories.map({ $0.engines.filter({ (eng: Engine) -> Bool in
return (eng.owner != nil)
})})
print ("factories not owned by player")
print (filtered)
The output is as follows:
All factories: [f1, f2, f3]
p1 = Bob, engines: [f1 - engine]
#0, Owners: Optional([Bob])
#1, Owners: Optional([])
#2, Owners: Optional([])
factories not owned by player
[[f1 - engine], [], []]
The issue I'm having is the last filter code;
// filter all factories NOT owned by player
let filtered = factories.map({ $0.engines.filter({ (eng: Engine) -> Bool in
return (eng.owner != nil)
})})
This only returns factories where engines are not nil,
I wish to use:
return (eng.owner != p1)
But an error is returned;
error: cannot convert value of type 'Player' to expected argument type '_OptionalNilComparisonType'
return (eng.owner != p1)
I'm thus wondering, how can I filter a map of factories and only return a list of all factories where a given player does not own?
Many thanks
I think this is what you're looking for:
extension Factory {
func isOwned(by player: Player?) -> Bool {
return self.engines.contains(where: { $0.isOwned(by: player)} )
}
}
extension Engine {
func isOwned(by player: Player?) -> Bool {
return self.owner === player
}
}
let factoriesNotOwnedByP1 = factories.filter { !$0.isOwned(by: p1) }
And here are some other changes I would make to your existing code:
import Foundation
class Factory {
let name: String
var engines = [Engine]()
init(name: String) {
self.name = name
self.engines += (1...3).map{ _ in Engine(parent: self) }
}
var owners: [Player]? {
return self.engines
.lazy
.flatMap { engine in engine.owner.map{ (engine: engine, owner: $0) } }
.sorted { $0.owner.turnOrder < $1.owner.turnOrder }
.map { $0.owner }
}
func isOwned(by player: Player?) -> Bool {
return self.engines.contains(where: { $0.isOwned(by: player)} )
}
}
extension Factory: CustomStringConvertible {
var description: String { return self.name }
}
class Engine {
weak var parent: Factory?
weak var owner: Player?
init(parent: Factory) {
self.parent = parent
}
func setOwner(owner: Player) {
self.owner = owner
self.owner?.addEngine(engine: self)
}
}
extension Engine: CustomStringConvertible {
var description: String {
guard let parent = self.parent else { return "N/A" }
return ("\(parent.name) - engine")
}
}
class Player {
let name: String
var engines = [Engine]()
var turnOrder = 0
init(name: String) {
self.name = name
}
func addEngine(engine: Engine) {
self.engines.append(engine)
}
}
extension Player: CustomStringConvertible {
var description: String { return self.name }
}
let factories = [
Factory(name: "f1"),
Factory(name: "f2"),
Factory(name: "f3"),
]
let p1 = Player(name: "Bob")
factories.first?.engines.first?.setOwner(owner: p1)
print ("All factories: \(factories)")
print ("p1 = \(p1.name), engines: \(p1.engines)")
for (index, f) in factories.enumerated() {
print("#\(index), Owners: \(String(describing: f.owners))")
}
let factoriesNotOwnedByP1 = factories.filter { !$0.isOwned(by: p1) }
print("factories not owned by player: ")
print(factoriesNotOwnedByP1)
You need to tell swift what type .owner is:
let filtered = factories.map({ $0.engines.filter({ (eng: Engine) -> Bool in
return (eng.owner as? Player != p1)
})})
Edit: It might actually be the 'p1' that needs to be set as Player, the error message doesn't specify.
I have variables of type string, [int], bool
var books:[String] = ["Hobbit","LOTR","Fellowship"]
var chaptersToRead:[[Int]] = [[1],[1,3],[2,3]]
var read:[Bool] = [false,true,true]
I have function display() so that I can see details about all the books individually
func display() -> [[Any]] {
var output = [[Any]]()
for i in 0..<books.count {
output.append([books[i], chaptersToRead[i], read[i]])
}
return output
}
I like to store values of books in two arrays based on condition if chaptersToRead = 1, as follows
var booksAssigned:[String] = ["Hobbit","LOTR"]
var readStatus:[Bool] = [false,true]
I tried to get the above result by doing the following below which is not working. What am I doing wrong?
var booksAssigned:[String] = []
var readStatus:[Bool] = []
for (index, books) in display().enumerated()
{
if chaptersToRead.joined().contains(1)
{
//I am getting signal SIGABRT error here
booksAssigned = books as! [String]
}
}
for (index, status) in display().enumerated()
{
if chaptersToRead.joined().contains(1)
{
//I am getting signal SIGABRT error here
readStatus = status as! [Bool]
}
}
Try this:
var booksAssigned:[String] = []
var readStatus:[Bool] = []
for (index, chapters) in chaptersToRead.enumerated() {
if chapters.contains(1) {
booksAssigned.append(books[index])
readStatus.append(read[index])
}
}
print(booksAssigned)
print(readStatus)
Edit: Edited as per #Nirav D's suggestion.
Remove var keyword from if blocks. You have already declared those variables.
var booksAssigned:[Any] = []
var readStatus:[Any] = []
for (index, books) in display().enumerated()
{
if chaptersToRead.joined().contains(1)
{
booksAssigned = books
}
}
for (index, status) in display().enumerated()
{
if chaptersToRead.joined().contains(1)
{
readStatus = status
}
}
This works.