Related
I'm creating a picker in which a food product is selected from the array productList of type Product and I'm trying to create text boxes that output every variable stored in whichever product was selected. However, although the code is successfully built, the picker won't allow the user to make a selection. How can I fix this?
This is my code:
struct AddView : View {
#State var selectFood = 0
#ObservedObject var database : Database
var body: some View {
VStack{
Picker(selection: $selectFood, label: Text("What did you eat?")
.font(.title)
.fontWeight(.bold)
.foregroundColor(.white))
{
ForEach(database.productList) { (productList: Product) in
Text(productList.name)
}
}
.pickerStyle(MenuPickerStyle())
Spacer(minLength: 100)
ZStack {
Rectangle()
.frame(width: 350, height: 90, alignment: .center)
.foregroundColor(.white)
.opacity(0.25)
.offset(y: 40)
.cornerRadius(10)
Text("Calories: \(database.productList[selectFood].calories)")
}
Spacer()
Text("Enter")
Spacer()
}
.padding(.horizontal, 50)
}
}
This is the code where the array is stored:
struct Product : Identifiable {
var id = UUID()
var name : String
var calories : Double
var protein : Double
var carbs : Double
var fats : Double
var weight : Double
}
class Database : ObservableObject{
#Published var productList = [Product]()
init() {
self.productList = [
Product(name: "Apple", calories: 52, protein: 0.3, carbs: 14, fats: 0.2, weight: 80),
Product(name: "Avocado", calories: 160, protein: 2, carbs: 9, fats: 15, weight: 600),
Product(name: "Banana", calories: 89, protein: 1.1, carbs: 23, fats: 0.3, weight: 120),
Product(name: "Broccoli", calories: 34, protein: 2.8, carbs: 5, fats: 0.4, weight: 225),
Product(name: "Burger", calories: 264, protein: 13, carbs: 30, fats: 10, weight: 110),
Product(name: "Carrot", calories: 41, protein: 0.9, carbs: 10, fats: 0.2, weight: 61),
Product(name: "Cheerios", calories: 379, protein: 6, carbs: 83, fats: 5, weight: 40),
Product(name: "Cherry", calories: 50, protein: 1, carbs: 12, fats: 0.3, weight: 5),
Product(name: "Chicken Nuggets", calories: 302, protein: 16, carbs: 15, fats: 20, weight: 16),
Product(name: "Cocoa Pops", calories: 389, protein: 5, carbs: 85, fats: 2.9, weight: 45),
Product(name: "Corn Flakes", calories: 357, protein: 8, carbs: 84, fats: 0.4, weight: 45),
Product(name: "Crunchy Nuts", calories: 402, protein: 6, carbs: 82, fats: 5, weight: 45),
Product(name: "Eggs", calories: 196, protein: 14, carbs: 0.8, fats: 15, weight: 60),
Product(name: "Fries", calories: 312, protein: 3.4, carbs: 43, fats: 15, weight: 100),
Product(name: "Froasted Flakes", calories: 369, protein: 4, carbs: 89, fats: 1.7, weight: 45),
]
}
}
Type of selection should be same as type of Picker data block id or tag, so if you want selected index, then it should be like
struct AddView : View {
#State var selectFood = 0
#ObservedObject var database : Database
var body: some View {
VStack{
Picker(selection: $selectFood, label: Text("What did you eat?")
.font(.title)
.fontWeight(.bold)
.foregroundColor(.white))
{
ForEach(database.productList.indices, id: \.self) { i in // << here
Text(database.productList[i].name) // << and here
}
}
.pickerStyle(MenuPickerStyle())
// ... other code
I've created a list of food products with stored data such as calories, protein, category, etc. I've made an array of categories in order to categorize these products into groups and I'm now trying to reference this array when stating the data in each product, but when doing this (Apple variable in line 16) I get the error:
"Cannot use instance member 'categories' within property initializer; property initializers run before 'self' is available"
This is my code:
struct Product : Identifiable {
var id = UUID()
var productName : String
var calories : Double
var protein : Double
var carbs : Double
var fats : Double
var weight : Double
var category : String
}
struct Data {
#State var categories = ["Animal Products", "Dairy", "Fast Food", "Fruits", "Grains", "Nuts", "Vegetables", "Wheat"]
var Apple = Product(productName: "Apple", calories: 52, protein: 0.3, carbs: 14, fats: 0.2, weight: 80, category: categories[3])
var Avocado = Product(productName: "Avocado", calories: 160, protein: 2, carbs: 9, fats: 15, weight: 600, category: <#T##String#>)
var Banana = Product(productName: "Banana", calories: 89, protein: 1.1, carbs: 23, fats: 0.3, weight: 120, category: <#T##String#>)
var Broccoli = Product(productName: "Broccoli", calories: 34, protein: 2.8, carbs: 5, fats: 0.4, weight: 225, category: <#T##String#>)
var Burger = Product(productName: "Burger", calories: 264, protein: 13, carbs: 30, fats: 10, weight: 110, category: <#T##String#>)
var Carrot = Product(productName: "Carrot", calories: 41, protein: 0.9, carbs: 10, fats: 0.2, weight: 61, category: <#T##String#>)
var Cheerios = Product(productName: "Cheerios", calories: 379, protein: 6, carbs: 83, fats: 5, weight: 40, category: <#T##String#>)
var Cherry = Product(productName: "Cherry", calories: 50, protein: 1, carbs: 12, fats: 0.3, weight: 5, category: <#T##String#>)
}
How can I include this array inside of the Product list?
It looks like categories are predefined, then here is a solution for your case - make categories static constant:
struct Data {
static let categories = ["Animal Products", "Dairy", "Fast Food", "Fruits", "Grains", "Nuts", "Vegetables", "Wheat"]
var Apple = Product(productName: "Apple", calories: 52, protein: 0.3,
carbs: 14, fats: 0.2, weight: 80, category: categories[3])
// ... others
}
I have a chart built with react-chartjs-2. The chart displays when I enter the values for data directly in the datasets like this.
datasets: [
{
label: "Active Countries",
backgroundColor: gradientFill,
borderColor: "#2CA8FF",
pointBorderColor: "#FFF",
pointBackgroundColor: "#2CA8FF",
pointBorderWidth: 2,
pointHoverRadius: 4,
pointHoverBorderWidth: 1,
pointRadius: 4,
fill: true,
borderWidth: 1,
data: [80, 99, 86, 96, 123, 85, 100, 75, 88, 90, 123, 155]
}
]
However, I want to replace the value of the data in the datasets with a monthly transaction derived from transactions recorded in my database. The code for my total transaction for each month Jan to Dec ( One for each month) is like this.
const januaryTransactions = transactions.filter(i => {
const date = new Date(i.date);
return date.getMonth() === January && date.getYear() === relevantYear;
}).reduce((prev, curr) => prev + parseFloat(curr.amount), 0)
My question is how do I replace for example the value at in index 0 [80, 99, 86, 96, 123, 85, 100, 75, 88, 90, 123, 155] which is 80 with januaryTransactions .
Any Pointers are mostly appreciated!!
Is it possible to map an array with a function that takes in two arguments?
Something like this:
let arr = [2,5,1,4,8,4]
let bankRateArr = arr.map(BankRate.init(amount:interestRate:))
class BankRate {
let amount: Int
let interestRate: Float
init(amount: Int, interestRate: Float) {
self.amount = amount
self.interestRate = interestRate
}
}
If you want to pass the same interest rate to all values, you can do:
class BankRate: CustomStringConvertible {
let amount: Int
let interestRate: Float
init(amount: Int, interestRate: Float){
self.amount = amount
self.interestRate = interestRate
}
var description: String {
return "amount: \(amount), rate: \(interestRate)"
}
}
let arr = [2, 5, 1, 4, 8, 4]
let bankRateArr = arr.map { BankRate(amount: $0, interestRate: 0.04) }
print(bankRateArr)
Output:
[amount: 2, rate: 0.04, amount: 5, rate: 0.04, amount: 1, rate: 0.04, amount: 4, rate: 0.04, amount: 8, rate: 0.04, amount: 4, rate: 0.04]
If you want each to have their own, you can do it with tuples:
let arr2 = [(2, 0.04), (5, 0.07), (1, 0.1), (4, 0.035), (8, 0.25), (4, 0.2)]
let bankRateArr2 = arr2.map { BankRate(amount: $0.0, interestRate: Float($0.1)) }
print(bankRateArr2)
Output:
[amount: 2, rate: 0.04, amount: 5, rate: 0.07, amount: 1, rate: 0.1, amount: 4, rate: 0.035, amount: 8, rate: 0.25, amount: 4, rate: 0.2]
And thanks to Martin R, the 2nd example can be shorted a bit as:
let arr3: [(Int, Float)] = [(2, 0.04), (5, 0.07), (1, 0.1), (4, 0.035), (8, 0.25), (4, 0.2)]
let bankRateArr3 = arr3.map(BankRate.init)
print(bankRateArr3)
Assuming that you will have two separate arrays
let amounts = [2, 5, 1, 4 ,8 ,4]
let rates: [Float] = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]
you could use
let bankRates = zip(amounts, rates).map(BankRate.init)
I have an array of about 860 items, I would like to turn it into a hash, the array structure is (Key1, value 1, value2, value 3, Key2, value 1, value 2, value 3........... etc etc) except in real life it looks like (Peter, 150, 39, 345, John, 123, 450, 402, Mary, 145, 345, 506............). Original these are four arrays which I have transposed together, so I could reach the same end goal by starting from 4 independent arrays.
I would like
Hash {Peter=> [150, 39, 345], John=>[123,450,402], Mary => [145,345,506] etc etc
I feel there should be a nice neat way to do this, but it eludes me, possibly because I don't know enough ruby.
One more way:
l = ['Peter', 150, 39, 345, 'John', 123, 450, 402, 'Mary', 145, 345, 506]
Hash[*l.each_slice(4).flat_map { |a, *b| [a, b] }]
#=> {"Peter"=>[150, 39, 345],
# "John" =>[123, 450, 402],
# "Mary" =>[145, 345, 506]}
If you already have arrays, solution will be even more simple:
arrays = [array_1, array_2, array_3]
Hash[*arrays.flat_map {|a, *b| [a, b]} ]
#=> {"Peter"=>[150, 39, 345], "John"=>[123, 450, 402], "Mary"=>[145, 345, 506]}
arr = ['Peter', 150, 39, 345, 'John', 123, 450, 'Mary', 145, 345, 506, 222]
arr.slice_before {|a| String === a }.
each_with_object({}) {|(f,*rest), h| h[f] = rest}
#=> {"Peter"=>[150, 39, 345], "John"=>[123, 450], "Mary"=>[145, 345, 506, 222]}
Enumerable#slice_before made its debut in Ruby v2.3. One could alternatively use Enumerable#slice_when, which was new in v2.2.
Here's are a couple more ways that work with earlier versions of Ruby.
arr.chunk { |e| String === e }.
each_slice(2).
with_object({}) { |((_,(k)),(_,v)), h| h[k] = v }
and
a = []
enum = arr.to_enum
loop do
o = enum.next
(String === o) ? a << [o,[]] : a.last.last << o
end
Hash[a]
If you know that the data is always in sets of 4:
l = ['Peter', 150, 39, 345, 'John', 123, 450, 402, 'Mary', 145, 345, 506]
h = {}
l.each_slice(4).each do |chunk|
h[chunk[0]] = chunk[1..chunk.length]
end
Alternately, if you know the keys are always strings:
l = ['Peter', 150, 39, 345, 'John', 123, 450, 402, 'Mary', 145, 345, 506]
h = {}
current_key = ""
l.each do |item|
if item.is_a? String
current_key = item
h[item] = []
else
h[current_key] << item
end
end
Basically, you need to work out how to slice the array appropriately and then loop through.
Also if you've already got them as separate arrays you could easily just:
array_1 = ['Peter', 150, 39, 345]
array_2 = ['John', 123, 450, 402]
array_3 = ['Mary', 145, 345, 506]
h = {}
[array_1,array_2, array_3].each do |a|
h[a[0]] = a[1:a.length]
end
# One line version:
[array_1,array_2, array_3].map{|a| h[a[0]] = a[1..a.length]}
Which I'd argue is the cleanest method.
Original these are four arrays which I have transposed together, so I could reach the same end goal by starting from 4 independent arrays.
I assume you have something like this to start with:
a = ["Peter", "John", "Mary"]
b = [150, 123, 145]
c = [39, 450, 345]
d = [345, 402, 506]
You can combine the "value" arrays via zip:
values = b.zip(c, d)
#=> [[150, 39, 345], [123, 450, 402], [145, 345, 506]]
And add the "key" array via another zip:
a.zip(values)
#=> [["Peter", [150, 39, 345]], ["John", [123, 450, 402]], ["Mary", [145, 345, 506]]]
This is already the correct structure, so we can just call to_h:
a.zip(values).to_h
#=> {"Peter"=>[150, 39, 345], "John"=>[123, 450, 402], "Mary"=>[145, 345, 506]}
Of course you don't need the intermediate variable, a.zip(b.zip(c, d)).to_h returns the same result.
Try this
l = ['Peter', 150, 39, 345, 'John', 123, 450, 402, 'Mary', 145, 345, 506]
l.each_slice(4).to_a.inject({}){|acc, temp| acc[temp.first]=temp.last(3); acc}
#=>{"Peter"=>[150, 39, 345], "John"=>[123, 450, 402], "Mary"=>[145, 345, 506]}