swift map filter reduce not working for $0.parameter swiftui

huangapple go评论64阅读模式
英文:

swift map filter reduce not working for $0.parameter swiftui

问题

我试图获取列表中某些元素的总和。每个元素都有一个名为quantity的Int属性。

MyElement:

class EmbeddedElement : EmbeddedRealmObject {
    var totalPrice: Double = 0.0
    var discount: Double = 0.0
    var quantity: Int = 1
}

我试图获取元素总价格的总和。

myElementsList 包含独特的元素。

问题在于,每当我尝试将 totalPrice 值与 $0.quantity 相乘时,它无法构建。出现以下错误:

“编译器无法在合理的时间内对此表达式进行类型检查;请尝试将表达式拆分为不同的子表达式”

例如,以下方式不起作用(与 $0.quantity 相乘时):

Text("\((myElementsList.map({($0.totalPrice-($0.totalPrice*$0.discount/100))*$0.quantity}).reduce(0,+)), specifier: "%.2f") dollar ")

但与简单数字 5 或任何其他数字相乘则可以正常工作:

Text("\((myElementsList.map({($0.totalPrice-($0.totalPrice*$0.discount/100))*5}).reduce(0,+)), specifier: "%.2f") dollar ")

为什么会发生这种情况?是因为 $0.quantity 是 Int 值吗?我尝试过进行类型转换,但没有任何作用。有什么解决方案吗?

英文:

I am trying to get the sum of some elements from my list.
Every element has a Int property called quantity.

MyElement:

class EmbeddedElement : EmbeddedRealmObject {
    var totalPrice: Double = 0.0
    var discount: Double = 0.0
    var quantity: Int = 1
  }

I am trying to get the sum of the total price of the elements.

The myElementsList has unique elements.

The problem is that whenever I try to multiple the totalPrice value with $0.quantity it does not build. With the error:

The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

For example this does not work (when multiplying with $0.quantity):

Text("\((myElementsList.map({($0.totalPrice-($0.totalPrice*$0.discount/100))*$0.quantity}).reduce(0,+)), specifier: "%.2f") dollar ")

But multiplying by simple number 5 or any other number works fine:

Text("\((myElementsList.map({($0.totalPrice-($0.totalPrice*$0.discount/100))*5}).reduce(0,+)), specifier: "%.2f") dollar ")

Why this happens? It is because that $0.quantity its a Int value? I tried casting but nothing worked. Any solution?

答案1

得分: 1

在计算的第一部分中,你正在对双精度数进行乘法运算,而计算中的所有内容都应该是双精度数。如果你将整数初始化为双精度数,例如:

Double($0.quantity)

在Playgrounds中,这种数学运算是有效的。此外,最好在运算符周围留有空格,因为这可能会引发一些警告。

class EmbeddedElement  {
    var totalPrice: Double = 5.0
    var discount: Double = 3.2
    var quantity: Int = 1
}

let element = EmbeddedElement()

var array = Array(repeatElement(element, count: 500))

let sum = array.map {
    ($0.totalPrice - ($0.totalPrice * $0.discount / 100)) * Double($0.quantity)
}.reduce(0,+)

对于第二个计算中的静态数字5,它之所以有效是因为它知道计算是双精度数,结果将是一个双精度数,因此它实际上将5初始化为双精度数。

当处理价格和货币时,你可能需要考虑使用Decimal类型,因为如你在简单示例中所见,双精度数可能导致很长的小数位数,并需要四舍五入,这可能会在大量数值上引发轻微的差异。

我猜你看到的错误信息比较模糊,因为问题相当复杂,类型系统难以提供有意义的解释。

英文:

You are multiplying doubles in the first part of the calc and everything in the calc should be Doubles. It works if you "initialise" the Int as a double, eg.

Double($0.quantity)

The maths works fine in playgrounds with this. It also is better to leave spaces around the operators also as I had a couple of flags on this.

class EmbeddedElement  {
var totalPrice: Double = 5.0
var discount: Double = 3.2
var quantity: Int = 1
}

let element = EmbeddedElement()

var array = Array(repeatElement(element, count: 500))

let sum = array.map {
	($0.totalPrice - ($0.totalPrice*$0.discount / 100)) * Double($0.quantity)
}.reduce(0,+)

swift map filter reduce not working for $0.parameter swiftui

With the static 5 in the second calc that works because it knows the calc is doubles and the result will be a double, so it effectively "initialises" the 5 as a double.

When dealing with prices and money you may want to look in to Decimal type as you can see with my simple example, doubles can lead to long decimal places and need for rounding which can cause minor discrepancies over a lot of values.

I guess the error you are seeing was vague as it is pretty complicated and the type system is struggling to provide something meaningful.

答案2

得分: 1

I think the code in the question is mostly correct. I cannot guarantee the math equation is right, but the code is sound. Here's how to troubleshoot

Take your initial code:

Text("\(myElementsList.map({($0.totalPrice-($0.totalPrice*$0.discount/100))*$0.quantity}).reduce(0,+), specifier: "%.2f") dollar ")

Let's break it down a bit into pieces so it can be examined, remove the formula, leaving the text formatting code:

Text("\(    , specifier: "%.2f") dollar ")

and then the equation:

let y = myElementsList.map({($0.totalPrice-($0.totalPrice*$0.discount/100))*$0.quantity}).reduce(0,+)

Ah, now the issue is obvious; the error is multiplying a double by an Int as mentioned in my comment. Fixing the equation is easy! Cast the Int to a Double and then the calculation works:

let y = myElementsList.map({($0.totalPrice-($0.totalPrice*$0.discount/100))*Double($0.quantity)}).reduce(0,+)

Not too complex!

Then insert that back into the Text code:

Text("\(y, specifier: "%.2f") dollar ")

And it works.

Note:

See the comment about using Doubles from currency in Hongtron's answer and my follow-up comment. Use Decimal128.

print(1.03 - 0.42)

outputs:

0.6100000000000001     (this is inaccurate!)

whereas

let a = Decimal128(floatLiteral: 1.03)
let b = Decimal128(floatLiteral: 0.42)
let c = a - b
print(c.decimalValue)

outputs:

0.61  (good!)
英文:

I think the code in the question is mostly correct. I cannot guarantee the math equation is right, but the code is sound. Here's how to troubleshoot

Take your initial code

Text("\((myElementsList.map({($0.totalPrice-($0.totalPrice*$0.discount/100))*$0.quantity}).reduce(0,+)), specifier: "%.2f") dollar ")

Let's break it down a bit into pieces so it can be examined, remove the formula, leaving the text formatting code

Text("\((    ), specifier: "%.2f") dollar ")

and then the equation

let y = myElementsList.map({($0.totalPrice-($0.totalPrice*$0.discount/100))*$0.quantity}).reduce(0,+))

ah - now issue is obvious; the error is multiplying a double by an Int as mentioned in my comment. Fixing the equation is easy! Cast the Int to a Double and then the calculation works.

let y = myElementsList.map({($0.totalPrice-($0.totalPrice*$0.discount/100))*Double($0.quantity)}).reduce(0,+)

Not too complex!

Then insert that back into the Text code

Text("\((y), specifier: "%.2f") dollar ")

And it works.

Note:

See the comment about using Doubles from currency in Hongtron's answer and my followup comment. Use Decimal128.

print(1.03 - 0.42)

outputs:

0.6100000000000001     (this is inaccurate!)

whereas

let a = Decimal128(floatLiteral: 1.03)
let b = Decimal128(floatLiteral: 0.42)
let c = a - b
print(c.decimalValue)

outputs:

0.61  (good!)

答案3

得分: 1

如评论中所提到的,你应该在这里使用计算属性,这不仅有助于编译器,还有助于获得更易理解的代码。

在这里,我将存储属性的类型从Double更改为Decimal,这就是这个答案中使用的类型。

首先,我创建了一个计算属性,用于计算金额,还更改了公式中折扣的处理方式,因为我认为这样更易读:

extension EmbeddedElement {
    var netAmount: Decimal {
        totalPrice * (1 - discount / 100.0) * Decimal(quantity)
    }
}

然后,我们可以在视图中使用这个计算属性,结合reduce()来计算总金额,但再次为清晰起见,我更喜欢使用计算属性,而不是在Text组件内部进行计算:

var totalAmount: Decimal {
    myElementsList.reduce(0) { $0 + $1.netAmount }
}

因此,我们有一个更易阅读的Text组件:

Text(totalAmount.formatted(.number.precision(.fractionLength(2))))

为了进一步简化,格式化样式可以移动到一个常量中(在这里,我还添加了四舍五入):

private let amountFormat = Decimal.FormatStyle
    .number
    .precision(.fractionLength(2))
    .rounded(rule: .awayFromZero)

有了这个,我们可以得到:

Text(totalAmount.formatted(amountFormat))
英文:

As mentioned in the comments you should use computed properties here, not only to help the compiler but also for getting code that is easier to understand.

Here I have changed the type of the stored properties from Double to Decimal so that is what is used in this answer.

First I created a computed property that calculates the amount, I also changed the handling of the discount in the formula since I think it reads better this way

extension EmbeddedElement {
    var netAmount: Decimal {
        totalPrice * (1 - discount / 100.0) * Decimal(quantity)
    }
}

Then we can use this in the view together with reduce() to calculate the total amount but once again I prefer a computed property for clarity instead of doing the calculation inside the Text component

var totalAmount: Decimal {
    myElementsList.reduce(0) { $0 + $1.netAmount }
}

So then we have a much more readable Text component

Text(totalAmount.formatted(.number.precision(.fractionLength(2))))

To simplify it even further the formatting style could be moved into a constant (here I also added rounding)

private let amountFormat = Decimal.FormatStyle
    .number
    .precision(.fractionLength(2))
    .rounded(rule: .awayFromZero)

With that the we get

Text(totalAmount.formatted(amountFormat))

huangapple
  • 本文由 发表于 2023年3月7日 02:07:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/75654337.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定