apple swift - referencing an array from a variable - arrays

newbie to swift, a contrived example.
I have an array of menu categories, which in turn have menu items
I want to loop through the categories and then read the elements of the sub-array of items. Don't know the syntax to reference the element as an array
ERROR at line 10, col 18: type 'String' does not conform to protocol 'Sequence'
for (item) in offering {
var menuOffering = ["drinks" , "breakfastEntree"]
var drinks: Set = ["water","milk","coffee","tea","diet coke"]
var breakfastEntree: Set = ["eggs", "bacon", "sausage","pancakes"]
for ( offering) in menuOffering {
print ("\(offering)")
for (item) in offering { ERROR
print ("\(item)")
}
}

menuOffering is a String array because you have defined it in quotes. In this case drinks and breakfastEntree. So you are trying to iterate over a String and getting error. Instead, define your array menuOffering as follow:
var menuOffering = [drinks , breakfastEntree] // without quotes
Now it will be array of sets.
You modified code should be :
var drinks: Set = ["water","milk","coffee","tea","diet coke"]
var breakfastEntree: Set = ["eggs", "bacon", "sausage","pancakes"]
var menuOffering = [drinks , breakfastEntree]
for ( offering) in menuOffering {
for (item) in offering {
print ("\(item)")
}
}

menuOffering is an Array of Sets. Without the quotes, the variable names now access the sets' contained values and they can be iterated over in the nested form you'll see below.
let drinks: Set = ["water", "milk", "coffee", "tea", "diet coke"]
let breakfastEntree: Set = ["eggs", "bacon", "sausage", "pancakes"]
let menuOffering = [drinks, breakfastEntree]
for offering in menuOffering {
for item in offering {
print(item)
}
}
You can also print variables out I did above if you are not trying to include a variable name in a String as below:
print("string: \(variableName)")
Make sure not to include a space between "print" and the parentheses.

Related

Chaining filter and map array by dictionary values in Swift

how can I use this type of chaining
let numbers = [20,17,35,4,12]
let evenSquares = numbers.filter{$0 % 2 == 0}.map{$0 * $0}
in such usecase
I have array of objects, I want to filter it by keys of external dictionary and then assign to those filtered objects value of dictionary where object id = dictionary key and then sort result object by dictionary value
here is code I have now:
let scoreDict: [String: Double]
var objects: [Object]
var filteredObjects = objects.filter { scoreDict.keys.contains.contains($0.id.uuidString.lowercased()) }
// .. and what next?
var scoresAssigned = filteredObjects.map { $0.score = scoreDict[$0.id.uuidString.lowercased()] } // This do not compile ""Cannot assign to property: '$0' is immutable"
Assuming Object is a struct, based on the error message...
This just shows that higher-order functions shouldn't be used everywhere. map transforms each element in a sequence into something else. So instead of assigning score, you need to return a new Object with its score changed.
var scoresAssigned = filteredObjects.map { $0.withScore(scoreDict[$0.id.uuidString.lowercased()]) }
withScore will look something like:
func withScore(_ score: Int) -> Object {
var copy = self
copy.score = score
return copy
}
But, if you just want to assign the score a new value, I recommend a simple for loop.
for i in 0..<filteredObjects.count {
filteredObjects[i].score = scoreDict[$0.id.uuidString.lowercased()]
}
Also note that you only need to access the dictionary once. If it's nil, the key doesn't exist.
var filteredObjects = [Object]()
for i in 0..<objects.count {
if let score = scoreDict[$0.id.uuidString.lowercased()] {
objects[i].score = score
filteredObjects.append(objects[i])
}
}

Search array for a value [swift]

Just started learning swift, spent a few hours trying to figure this out by searching here but no luck so far.
I have an array, created like this:
class ProbabilitiesClass {
var list = [Odds]()
init() {
list.append(Odds(dateInit: -35, oddsTodayInit: "0,01", sevenDaysInit: "0,2"))
list.append(Odds(dateInit: -34, oddsTodayInit: "0,01", sevenDaysInit: "0,3"))
list.append(Odds(dateInit: -33, oddsTodayInit: "0,02", sevenDaysInit: "0,4"))
I want to search first parameter of this array for an integer and return its index.
Tried this
if let i = Odds.firstIndex(where: { $0.hasPrefix(differenceInDays) }) {
print("index is \([i])")
}
It returns error:
Type 'Odds' has no member 'firstIndex'
The end goal is to return second and third parameters of that index.
Update: I defined Odds like this:
import Foundation
class Odds {
let dateText : Int
let oddsToday : String
let odds7Days : String
init(dateInit: Int, oddsTodayInit: String, sevenDaysInit : String) {
dateText = dateInit
oddsToday = oddsTodayInit
odds7Days = sevenDaysInit
}
}
You could do it like so:
let p = ProbabilitiesClass()
let differenceInDays = -34
if let i = p.list.firstIndex(where: { $0.dateText == differenceInDays }) {
print("index is \([i])") //index is [1]
}
Look for the index in the list property of an instance of ProbabilitiesClass. And like the error message says: The class Odds is not an array to use the method firstIndex(where:) on it.
If you want to use the properties of the first element which has its dateInit equal to differenceInDays, then you could do it like so:
if let first = p.list.first(where: { $0.dateText == differenceInDays }) {
print("oddsTodayInit =", first.oddsToday)
print("sevenDaysInit =", first.odds7Days)
}
It uses this function first(where:).

Filter multiple arrays with one condition

I have a tableView with different kinds of infos, each coming from a different array.
I could not work with dictionaries because then the list would have been unordered and I could not work with classes, because I have different lists with all kinds of dynamic entries (properties are always different etc.)
Here my problem:
I want to implement a search function. But when I use the filter function for one array, it changes of course based on the implemented condition but the other 5 stay the same => I can't reload the tableView because the array information does not match anymore ...
Here the arrays:
var categoryItemUIDs = [String]()
var categoryItemDescriptions = [String]()
var categoryItemLfdNrs = [Int]()
var categoryGivenOuts = [Bool]()
var categoryGivenTos = [String]()
var categoryGivenAts = [String]()
var categoryStorageLocations = [String]()
In the tableView(cellForRowAtIndexPath method):
cell.customTextLabel?.text = categoryItemLfdNrs[indexPath.row]
cell.customDetailTextLabel.text = categoryItemDescriptions[indexPath.row]
Here the searchBar(textDidChange) method:
self.categoryItemDescriptions.filter { $0.lowercased().contains(searchText.lowercased()) }
Now I get an array back with reduced size, but all the other arrays stay the same... Is there maybe another way to avoid this problem? I already tried type aliases but it did not work out.
I would appreciate any help!
Kind regards,
When it goes to such a big count of arrays, the time for your specific type comes.
The simple solution is to create something like
struct Category {
var uid: String
var description: String
// ...
var storageLocation: String
}
The you have simply something like
var items: [Category]
And you can still do simple things in cellForRowAtIndexPath
cell.customTextLabel?.text = items[indexPath.row].lfdnrs
cell.customDetailTextLabel.text = items[indexPath.row].description
And only 1 array to filter
items.filter { $0.description.lowercased().contains(searchText.lowercased()) }
So overall advice is to solve different problem (here I suggested the solution of the having your data in the app problem instead of filtering multiple arrays with one condition)
try
var categoryItemUIDs = ["aaa","bbb","ccc"]
var categoryItemDescriptions = ["ddd","eee","fff"]
var categoryItemLfdNrs = [0,1,2]
struct data {
var id = ""
var desc = ""
var item = 0
init(id :String, desc:String, item:Int)
{
self.id = id
self.desc = desc
self.item = item
}
}
//var cat = [data]()
//for i in 0..<categoryItemUIDs.count {
// cat.append(data(id:categoryItemUIDs[i], desc:categoryItemDescriptions[i],item:categoryItemLfdNrs[i] ))
//}
//more swift
let cat = (0..<categoryItemUIDs.count).map { (i) -> data in
return data(id:categoryItemUIDs[i], desc:categoryItemDescriptions[i],item:categoryItemLfdNrs[i] )
}
print (cat)
let catFilter = cat.filter { $0.id == "aaa" }
print (catFilter)

Modify an array element after finding it in swift does not work

I wrote a model like this as an exercise :
struct Store {
var name : String
var bills : Array<Bill>
var category : Category?
}
struct Bill {
var date : String
var amount : Float
}
struct Category {
var name : String
var tags : Array<String>
}
and when I'm searching if a store already exist to add a bill to it instead of creating a new store, my code doesn't work. It acts like if the result of the search is a copy of the Array element . I would like to have a reference.
var stores : Array <Store> = Array()
for billStatment in billStatements! {
let billParts = billStatment.split(separator: ",")
if billParts.count > 0 {
let bill : Bill = Bill(date:String(billParts[0]), amount: Float(billParts[2])!)
var store : Store = Store(name:String(billParts[1]), bills: [bill], category: nil)
if var alreadyAddedStore = stores.first(where: {$0.name == String(billParts[1])}) {
alreadyAddedStore.bills.append(bill)
print("yeah found it \(alreadyAddedStore)") // the debugger breaks here so I know the "first" method is working. If I print alreadyAddedStore here I have one more element, that's fine.
} else {
stores.append(store)
}
}
}
print("\(stores.count)") // If I break here for a given store that should contains multiple elements, I will see only the first one added in the else statement.
Can anyone tell me what I am doing wrong?
As already noted, you're confusing value (struct) semantics with reference (class) semantics.
One simple fix would be the change stores to a dictionary with the name as your key:
var stores : Dictionary<String, Store> = [:]
and use it like this:
if(stores[store.name] == nil) {
stores[store.name] = store
}
else {
stores[storeName].bills.append(bill)
}

How to filter an array to correspond other array

I've two arrays:
var filteredTitles = [String]()
var filteredTypes = [String]()
I filter the first array as a part of using searchbar. The order of the elements might change completely. However, I can't filter the second array the same way I did the first one, because I don't want to take it in to count when searching. But I would like for the second array to be in the same order as the first one. So, to recap. How can I filter an array to match another one by indexes perfectly?
An example:
var filteredArray = ["One", "Two", "Three"]
//Sort the below array to ["1", "2", "3"], the order of the upper array
var toBeFilteredArray = ["2", "1", "3"]
WITHOUT using alphabetical or numerical order, as that won't do in this case.
EDIT:
TO Russell:
How do I sort the titles like this:
// When there is no text, filteredData is the same as the original data
// When user has entered text into the search box
// Use the filter method to iterate over all items in the data array
// For each item, return true if the item should be included and false if the
// item should NOT be included
searchActive = true
filteredData = searchText.isEmpty ? original : original.filter({(dataString: String) -> Bool in
// If dataItem matches the searchText, return true to include it
return dataString.range(of: searchText, options: .caseInsensitive) != nil
})
don't have two arrays - have a single array of a custom type, containing both variables that you need
Define your struct
struct MyCustomData
{
var dataTitle : String = ""
var dataType : String = ""
}
and then declare it
var dataArray : [MyCustomData] = []
populate it and sort it where required - I have populated in reverse order just so that we can see it being sorted
dataArray.append(MyCustomData(dataTitle: "Third", dataType: "3"))
dataArray.append(MyCustomData(dataTitle: "Second", dataType: "2"))
dataArray.append(MyCustomData(dataTitle: "First", dataType: "1"))
let filteredArray = dataArray.sorted {$0.dataTitle < $1.dataTitle}
for filteredElement in filteredArray
{
print("\(filteredElement.dataTitle), \(filteredElement.dataType)")
}
// or, to print a specific entry
print("\(filteredArray[0].dataTitle), \(filteredArray[0].dataType)")
An example of keeping two separate arrays in sync using zip:
let titles = ["title1", "title3", "title4", "title2"]
let types = ["typeA", "typeB", "typeC", "typeD"]
let zipped = zip(titles, types)
// prints [("title4", "typeC"), ("title2", "typeD")]
print(zipped.filter { Int(String($0.0.characters.last!))! % 2 == 0 })
You can use map on the filtered result to get back two separate filtered arrays for the titles and types.

Resources