# Arithmetic operations with NamedTuples

**URL:** <https://discourse.julialang.org/t/arithmetic-operations-with-namedtuples/107140>\
**Category:** General Usage\
**Tags:** question, namedtuple, staticarrays\
**Created:** [December 5, 2023, 2:22am UTC](https://discourse.julialang.org/t/arithmetic-operations-with-namedtuples/107140 "2023-12-05T02:22:32Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![fusion809](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fusion809/32/6080_2.png) [@fusion809](https://discourse.julialang.org/u/fusion809)\
**Post date:** [December 5, 2023, 2:22am UTC](https://discourse.julialang.org/t/arithmetic-operations-with-namedtuples/107140/1 "2023-12-05T02:22:32Z")

</div>

I often use Julia to calculate the nutrition of my meals. I know there are calculators for this online, but I prefer doing this myself in Julia. I normally do this by first defining NamedTuples with the nutritional information per 100g of each ingredient of the meal, then creating a NamedTuple with the quantity (in units of 100g) of each ingredient and then multiplying the elements of these tuples together and adding them up for each bit of nutritional information I want to calculate. For instance, here’s a program I created to calculate the nutritional information of a vegan chocolate banana bread I considered making:

```julia
olive_oil = (cost=43/40, energy=3390, protein=0, fat=91.0, saturated_fat=13, carbohydrate=0, sugars=0, dietary_fibre=0, sodium=0)
wholemeal_spelt_flour = (cost=7.5/7.5, energy=1470, protein=13.5, fat=3.1, saturated_fat=0.5, carbohydrate=62.1, sugars=0.8, dietary_fibre=8.1, sodium=13)
lakanto_monkfruit_sweetener = (cost=16/5, energy=115, protein=0, fat=0, saturated_fat = 0, carbohydrate = 0, sugars = 0, dietary_fibre = 0, sodium=0)
banana = (cost=0.72/0.98, energy=359, protein=1.4, fat=0.2, saturated_fat=0, dietary_fibre=2.2, carbohydrate=19.6, sugars=12.8, sodium=0)
vanilla_soy_milk = (cost=3/10, energy=247, protein=3.2, fat=1.5, saturated_fat=0.2, carbohydrate=8, sugars=4.9, dietary_fibre=0.1, sodium=45)
baking_powder = (cost=5.56/3, energy=0, protein=0, fat=0, saturated_fat=0, carbohydrate=0, sugars=0, dietary_fibre=0.1, sodium=0)
cocoa_powder = (cost=4/3.75, energy=1500, protein=17.7, fat=22.3, saturated_fat=14.1, carbohydrate=9.4, sugars=0.7, dietary_fibre=27, sodium=18)
cinnamon = (cost=1.8/0.32, energy=1210, protein=11.1, fat=9, saturated_fat=4.33, carbohydrate=22, sugars=5.2, dietary_fibre=39.7, sodium=59)

quantities = (olive_oil = 0.73/8, wholemeal_spelt_flour = 3.1/8, lakanto_monkfruit_sweetener = 1.6/8, banana=1.96*3/8, vanilla_soy_milk = 1.29/8, baking_powder = 0.1/8, cocoa_powder = 0.3/8, cinnamon=0.05/8)

cost = olive_oil.cost * quantities.olive_oil + wholemeal_spelt_flour.cost * quantities.wholemeal_spelt_flour + lakanto_monkfruit_sweetener.cost * quantities.lakanto_monkfruit_sweetener + banana.cost * quantities.banana + vanilla_soy_milk.cost * quantities.vanilla_soy_milk + baking_powder.cost * quantities.baking_powder + cocoa_powder.cost * quantities.cocoa_powder + cinnamon.cost * quantities.cinnamon

energy = olive_oil.energy * quantities.olive_oil + wholemeal_spelt_flour.energy * quantities.wholemeal_spelt_flour + lakanto_monkfruit_sweetener.energy * quantities.lakanto_monkfruit_sweetener + banana.energy * quantities.banana + vanilla_soy_milk.energy * quantities.vanilla_soy_milk + baking_powder.energy * quantities.baking_powder + cocoa_powder.energy * quantities.cocoa_powder + cinnamon.energy * quantities.cinnamon

protein = olive_oil.protein * quantities.olive_oil + wholemeal_spelt_flour.protein * quantities.wholemeal_spelt_flour + lakanto_monkfruit_sweetener.protein * quantities.lakanto_monkfruit_sweetener + banana.protein * quantities.banana + vanilla_soy_milk.protein * quantities.vanilla_soy_milk + baking_powder.protein * quantities.baking_powder + cocoa_powder.protein * quantities.cocoa_powder + cinnamon.protein * quantities.cinnamon

fat = olive_oil.fat * quantities.olive_oil + wholemeal_spelt_flour.fat * quantities.wholemeal_spelt_flour + lakanto_monkfruit_sweetener.fat * quantities.lakanto_monkfruit_sweetener + banana.fat * quantities.banana + vanilla_soy_milk.fat * quantities.vanilla_soy_milk + baking_powder.fat * quantities.baking_powder + cocoa_powder.fat * quantities.cocoa_powder + cinnamon.fat * quantities.cinnamon

saturated_fat = olive_oil.saturated_fat * quantities.olive_oil + wholemeal_spelt_flour.saturated_fat * quantities.wholemeal_spelt_flour + lakanto_monkfruit_sweetener.saturated_fat * quantities.lakanto_monkfruit_sweetener + banana.saturated_fat * quantities.banana + vanilla_soy_milk.saturated_fat * quantities.vanilla_soy_milk + baking_powder.saturated_fat * quantities.baking_powder + cocoa_powder.saturated_fat * quantities.cocoa_powder + cinnamon.saturated_fat * quantities.cinnamon

carbohydrate = olive_oil.carbohydrate * quantities.olive_oil + wholemeal_spelt_flour.carbohydrate * quantities.wholemeal_spelt_flour + lakanto_monkfruit_sweetener.carbohydrate * quantities.lakanto_monkfruit_sweetener + banana.carbohydrate * quantities.banana + vanilla_soy_milk.carbohydrate * quantities.vanilla_soy_milk + baking_powder.carbohydrate * quantities.baking_powder + cocoa_powder.carbohydrate * quantities.cocoa_powder + cinnamon.carbohydrate * quantities.cinnamon

sugars = olive_oil.sugars * quantities.olive_oil + wholemeal_spelt_flour.sugars * quantities.wholemeal_spelt_flour + lakanto_monkfruit_sweetener.sugars * quantities.lakanto_monkfruit_sweetener + banana.sugars * quantities.banana + vanilla_soy_milk.sugars * quantities.vanilla_soy_milk + baking_powder.sugars * quantities.baking_powder + cocoa_powder.sugars * quantities.cocoa_powder + cinnamon.sugars * quantities.cinnamon

dietary_fibre = olive_oil.dietary_fibre * quantities.olive_oil + wholemeal_spelt_flour.dietary_fibre * quantities.wholemeal_spelt_flour + lakanto_monkfruit_sweetener.dietary_fibre * quantities.lakanto_monkfruit_sweetener + banana.dietary_fibre * quantities.banana + vanilla_soy_milk.dietary_fibre * quantities.vanilla_soy_milk + baking_powder.dietary_fibre * quantities.baking_powder + cocoa_powder.dietary_fibre * quantities.cocoa_powder + cinnamon.dietary_fibre * quantities.cinnamon

sodium = olive_oil.sodium * quantities.olive_oil + wholemeal_spelt_flour.sodium * quantities.wholemeal_spelt_flour + lakanto_monkfruit_sweetener.sodium * quantities.lakanto_monkfruit_sweetener + banana.sodium * quantities.banana + vanilla_soy_milk.sodium * quantities.vanilla_soy_milk + baking_powder.sodium * quantities.baking_powder + cocoa_powder.sodium * quantities.cocoa_powder + cinnamon.sodium * quantities.cinnamon

```

I am wondering whether there may be a more efficient way. Like I would prefer to be able to calculate all the nutritional information in one line with `olive_oil * quantities.olive_oil + wholemeal_spelt_flour * quantities.wholemeal_spelt_flour + lakanto_monkfruit_sweetener * quantities.lakanto_monkfruit_sweetener + ...`, but this isn’t possible it seems as I get errors that such an operation is not permitted. It seems that arithmetic operations are not defined on NamedTuples.

---

<div class="post-metadata">

**Author:** ![mleseach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mleseach/32/202840_2.png) [@mleseach](https://discourse.julialang.org/u/mleseach)\
**Post date:** [December 5, 2023, 3:19am UTC](https://discourse.julialang.org/t/arithmetic-operations-with-namedtuples/107140/2 "2023-12-05T03:19:37Z")

</div>

I think the best way is to defined a struct and create a method for addition and multiplication.

Also if the names are always in the same order you can convert to a tuple, broadcast operation, then convert back to the original named tuple.

```julia
Item = typeof(olive_oil)
Item(3.5 .* Tuple(olive_oil) .+ 0.5 .* Tuple(banana))

```

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [December 5, 2023, 3:39am UTC](https://discourse.julialang.org/t/arithmetic-operations-with-namedtuples/107140/3 "2023-12-05T03:39:00Z")

</div>

The abstract type `FieldVector` from StaticArrays.jl was designed for this. Subtyping it adds array methods to your struct, such as addition and subtraction, scaling, matrix multiplication, and so on. Combine it with `Base.@kwdef` for the keyword constructor and you’re good to go:

```julia
using StaticArrays

Base.@kwdef struct NutritionFacts <: FieldVector{9,Float64}
	cost::Float64
	energy::Float64
	protein::Float64
	fat::Float64
	saturated_fat::Float64
	carbohydrate::Float64
	sugars::Float64
	dietary_fibre::Float64
	sodium::Float64
end

```

```julia-repl
julia> olive_oil = NutritionFacts(
           cost=43/40,
           energy=3390,
           protein=0,
           fat=91.0,
           saturated_fat=13,
           carbohydrate=0,
           sugars=0,
           dietary_fibre=0,
           sodium=0,
       );

julia> wholemeal_spelt_flour = NutritionFacts(
           cost=7.5/7.5,
           energy=1470,
           protein=13.5,
           fat=3.1,
           saturated_fat=0.5,
           carbohydrate=62.1,
           sugars=0.8,
           dietary_fibre=8.1,
           sodium=13,
       );

julia> 5 * olive_oil + 27 * wholemeal_spelt_flour
9-element NutritionFacts with indices SOneTo(9):
    32.375
 56640.0
   364.5
   538.7
    78.5
  1676.7
    21.6
   218.7
   351.0

```

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [December 5, 2023, 4:20am UTC](https://discourse.julialang.org/t/arithmetic-operations-with-namedtuples/107140/4 "2023-12-05T04:20:29Z")

</div>

LabelledArrays.jl may be useful for this

---

<div class="post-metadata">

**Author:** ![GHTaarn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ghtaarn/32/216007_2.png) [@GHTaarn](https://discourse.julialang.org/u/GHTaarn)\
**Post date:** [December 5, 2023, 6:22am UTC](https://discourse.julialang.org/t/arithmetic-operations-with-namedtuples/107140/5 "2023-12-05T06:22:34Z")

</div>

Is there a simple way to display a `NutritionFacts` struct with its fieldnames or would one have to write ones own `display` function?

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [December 5, 2023, 6:57pm UTC](https://discourse.julialang.org/t/arithmetic-operations-with-namedtuples/107140/6 "2023-12-05T18:57:20Z")

</div>

I agree the display is unfortunate. I think it’s just a consequence of subtyping `AbstractVector`. Can’t find a workaround in the documentation. Seems like LabelledArrays does better in this respect:

```julia-repl
julia> using LabelledArrays

julia> SLVector(foo=3.0, bar=5.0)
2-element SLArray{Tuple{2}, Float64, 1, 2, (:foo, :bar)} with indices SOneTo(2):
 :foo => 3.0
 :bar => 5.0

```

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [December 5, 2023, 7:09pm UTC](https://discourse.julialang.org/t/arithmetic-operations-with-namedtuples/107140/7 "2023-12-05T19:09:59Z")

</div>

> <https://github.com/JuliaArrays/StaticArrays.jl/issues/1223>
>
> A \`FieldArray\` displays as an array, values only. It would be great if it showed… the field names too---named fields are presumably the reason to use a \`FieldArray\` in the first place.
> 
> \`\`\`julia
> julia\> using StaticArrays
> 
> julia\> Base.@kwdef struct Foo \<: FieldVector{2,Float64}
> bar::Float64
> baz::Float64
> end
> Foo
> 
> julia\> Foo(bar=3.0, baz=5.0)
> 2-element Foo with indices SOneTo(2):
> 3.0
> 5.0
> \`\`\`
> 
> Compare LabelledArrays.jl:
> 
> \`\`\`julia
> julia\> using LabelledArrays
> 
> julia\> SLVector(bar=3.0, baz=5.0)
> 2-element SLArray{Tuple{2}, Float64, 1, 2, (:bar, :baz)} with indices SOneTo(2):
> :bar =\> 3.0
> :baz =\> 5.0
> \`\`\`

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [December 5, 2023, 7:12pm UTC](https://discourse.julialang.org/t/arithmetic-operations-with-namedtuples/107140/8 "2023-12-05T19:12:28Z")

</div>

Alternatively, here’s a clean way to convert between FieldVector (good for computations) and NamedTuple (good for display) – the `getproperties()` function:

```julia
julia> using StaticArrays, ConstructionBase

julia> Base.@kwdef struct NutritionFacts <: FieldVector{9,Float64} ...

julia> olive_oil = NutritionFacts(...);

julia> olive_oil |> getproperties
(cost = 1.075, energy = 3390.0, protein = 0.0, fat = 91.0, saturated_fat = 13.0, carbohydrate = 0.0, sugars = 0.0, dietary_fibre = 0.0, sodium = 0.0)

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [December 6, 2023, 8:50pm UTC](https://discourse.julialang.org/t/arithmetic-operations-with-namedtuples/107140/9 "2023-12-06T20:50:52Z")

</div>

> [@aplavin](#):
>
> getproperties()

Without using the `ConstructionBase` package, in this case:

```julia
getprop(x) = NamedTuple{propertynames(x)}(x)

olive_oil |> getprop

(cost = 1.075, energy = 3390.0, protein = 0.0, fat = 91.0, saturated_fat = 13.0, carbohydrate = 0.0, sugars = 0.0, dietary_fibre = 0.0, sodium = 0.0)

```

> **And an alternative using pairs:**
>
> ```julia-auto
> getprop2(x) = propertynames(x) .=> x
> 
> olive_oil |> getprop2
> 
> 9-element SVector{9, Pair{Symbol, Float64}} with indices SOneTo(9):
> :cost => 1.075
> :energy => 3390.0
> :protein => 0.0
> :fat => 91.0
> :saturated_fat => 13.0
> :carbohydrate => 0.0
> :sugars => 0.0
> :dietary_fibre => 0.0
> :sodium => 0.0
> 
> ```
