andyferris / Dictionaries.jl

An alternative interface for dictionaries in Julia, for improved productivity and performance
Other
278 stars 28 forks source link

3 Feature Requests: 1) compounding 2) struct to dictionary 3) kwargs #111

Open Lincoln-Hannah opened 1 year ago

Lincoln-Hannah commented 1 year ago
using Dictionaries, Parameters, NamedTupleTools, Lazy

# 1-   Compounding 2 dictionaries.   
#       d1, d2 joined on values(d1)=keys(d2).    Resulting Dictionary maps keys(d1) to values(d2)

compound(d1::Dictionary,d2::Dictionary) = @>>    d1    filter(∈(keys(d2)))     map(y->d2[y])

#        allow the compound function to also take arguments that can be converted to dictionaries

compound(iterable1 ,iterable2 ) = compound( Dictionary(iterable1),  Dictionary(iterable2)   )

#         infix operator

⊚(d1,d2) = compound(d1,d2)      

# 2-   Create Dictionary from struct

DictfromStruct = Dictionary ∘ ntfromstruct

#         would prefer to add a method Dictionary(x::struct) but ::struct   doesn't exist.
#         (ntfromstruct uses isstructtype to throw error if input isn't a struct. But this is in the function body)

# 3-    Allow Dictionary to be kwargs in the same way Dict can.      e.g.   f(;  d... )

###########       EXAMPLE       ###################################################
###################################################################################

@enum Currency AUD GBP CAD JPY

FX_Rates = Dictionary( [AUD,GBP,CAD,JPY], [0.7,1.2,0.9,101] )

@with_kw struct Trade 
    C1::Currency
    C2::Currency
    other::Float
end

value( t::Trade; C1::Float, C2::Float) = t.other * C1 * C2

trade_1 = Trade( C1=AUD, C2=GBP, other=10)

#         I'd like the below to produce   Dictionary( [:C1,:C2],[0.7,1.2])
#         Infix operator calls compound function which converts trade_1 to Dictionary then compounds it with FX_Rates

trade_1 ⊚ FX_Rates     

#          using  #3-   I'd like to call the value function with

value( trade_1;   (trade_1 ⊚ FX_Rates)... )
andyferris commented 1 year ago

Interesting! Let me go in reverse order:

3 - I love the keyword argument idea! On the reverse side I had been toying with idea of overloading getproperty to let you fetch Symbol keys.

2 - Given 3, in recent versions of Julia users could type Dictionary(; struct...). Is that right? Given that, should we just document that rather than adding methods that have to infer if something is struct-ish or not?

1 - Is this the same operation as getindices from Indexing.jl?

For 1, I do think we need to solve this somehow. Sometimes I think about this in terms of tabular data - we have one-one joins, many-one joins and many-many joins. The one-one primary key to primary key join is just merge. The many-one foreign key to primary key join is like this "compound". I think many-many should generally rely on the user to reindex data - e.g. with group from SplitApplyCombine.jl or similar (or using innerjoin from SplitApplyCombine.jl).

Lincoln-Hannah commented 1 year ago

3- By overloading getproperty. Do you mean the ability to write Dict.key instead of Dict[:key] ? This is what DotMaps.jl does. It works for a Dict but not a Dictionary A nice feature to have sometimes.

2 - (; struct...) Doesn't work on 1.8.0. I use ntfromstruct() from NamedTupleTools.jl. So maybe could just document to use Dictionary ∘ ntfromstruct Would be great if it could be done in a Dictionary() method but I think "struct-ish" can only be determined in the function body (not in the first line where the types are specified.)

1- You're right getindices does this.
Though getindices( d1, d2 ) fails for any d2 value that not matching a d1 key. Could there be an option to exclude these and return a Dictionary with keys = intersect( keys(d1), values(d2) )

I really like the relational database analogy. I'm most interested in the PK-FK relationship. I'll add an example.

Lincoln-Hannah commented 1 year ago
using WhereTraits, NamedTupleTools

@traits Dictionary( x ) where isstructtype(typeof(x)) = Dictionary( ntfromstruct( x ))

Could do this