Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

map:build() function #151

Closed
michaelhkay opened this issue Sep 21, 2022 · 7 comments
Closed

map:build() function #151

michaelhkay opened this issue Sep 21, 2022 · 7 comments
Labels
Feature A change that introduces a new feature XQFO An issue related to Functions and Operators

Comments

@michaelhkay
Copy link
Contributor

michaelhkay commented Sep 21, 2022

This proposal replaces #110

map:build($input as item()*, 
                  $makeKey as function(item()) as xs:anyAtomicValue, 
                  $makeValue as function(item()) as item()*.
                  $duplicates as function(item()*, item()*) as item()*) as map(*)

The fourth argument is optional and defaults to fn:op(",").

The third argument is optional and defaults to fn:identity#1

Example 1:

map:build(//employee, ->{@ssn}, identity#1, ->($x,$y){error()})
Constructs an index of employees with the atomised @ssn attribute as key, and the employee element as the associated value, failing if there are any duplicates

This can be abbreviated to map:build(//employee, ->{@ssn}) if it is known that there are no duplicates, or if the default action on duplicates is acceptable.

Example 2:

map:build(//employee, ->{location/city}, ->{xs:decimal(salary)}, op(","))

Constructs a map whose keys are the locations of employees and whose associated values are the salaries of employees at that location. The effect of the $duplicates argument is that if there are several employees at the same location, their salaries are combined into a sequence (using the "," operator). If the last argument were op("+"), the map would contain the sum of the salaries for each location.

Rules

The function creates a map.

Informally, the function processes each item in the input sequence in turn in sequence order. It calls the $makeKey function on that item to obtain a key value, and the $makeValue function to obtain an associated value. If the key is not already present in the map, it creates a new key-value pair with that key and that value. If the key is already present, it combines the existing value for the key with the new value using the $duplicates function, and replaces the entry with this combined value.

More formally, the result of the function is the result of the following expression:

fold-left($input, map{}, ->($old, $next){
    let $key := $makeKey($next)
    let $value := $makeValue($next)
    return 
       if (map:contains($old, $key))
       then map:put($old, $key, $duplicates($map:get($key), $value))
       else map:put($old, $key, $value)
   })

NOTE: although defined to process the input sequence in order, the implementation may be optimised (for example to work in parallel) if it is known that the $duplicates function is symmetric, that is, if $duplicates($a, $b) produces the same result as $duplicates($b, $a).

@ChristianGruen ChristianGruen added XQFO An issue related to Functions and Operators Feature A change that introduces a new feature labels Sep 21, 2022
@dnovatchev
Copy link
Contributor

dnovatchev commented Sep 21, 2022

The fourth argument is optional and defaults to fn:op(",").

The third argument is optional and defaults to fn:identity#1

Can't we provide the defaults as part of the signature (and thus have just a single overload), now that we have the capability to have keyword-arguments (such as in %variadic("bounded") functions)?

@michaelhkay
Copy link
Contributor Author

michaelhkay commented Sep 21, 2022

Yes, we can now define defaults as part of the signature. But I haven't yet got all the machinery working (e.g XSD and XSLT changes) to make it happen. Also, I want to avoid introducing too many forwards dependencies into the specification process: if we can try to avoid proposals that depend on features we haven't yet specified or agreed, we're less likely to tie ourselves in knots.

@dnovatchev
Copy link
Contributor

Also, I want to avoid introducing too many forwards dependencies into the specification process: if we can try to avoid proposals that depend on features we haven't yet specified or agreed, we're less likely to tie ourselves in knots.

Concerns understood.

We already agreed to discuss and eventually approve the more independent (basic, fundamental) features before the rest. Why couldn't we get done with the variadic functions, before anything else?

And if we are not using our own new additions to XPath (such as variadic functions), the readers may decide that there is something "fishy" here ...

So, let us agree that later in the process we will update the document to use single overloads for variadic functions.

Can we agree on this, please?

@michaelhkay
Copy link
Contributor Author

Note that this proposed function replaces map:group-by as described in the current draft spec. The two-argument function map:build() is essentially identical to the proposed map:group-by; the two additional optional arguments greatly increase its power.

@dnovatchev
Copy link
Contributor

dnovatchev commented Oct 11, 2022

map:build($input as item()*, 
                  $makeKey as function(item()) as xs:anyAtomicValue, 
                  $makeValue as function(item()) as item()*.
                  $duplicates as function(item()*, item()*) as item()*) as map(*)

The fourth argument is optional and defaults to fn:op(",").

The third argument is optional and defaults to fn:identity#1

Excellent, we have just one single overload now!

The only thing I am still wishing, now that fn:op() is approved, is to have the above defined as (with default values added to the definition):

map:build($input as item()*, 
          $makeKey as function(item()) as xs:anyAtomicValue, 
          $makeValue as function(item()) as item()* :=  fn:identity#1,
          $duplicates as function(item()* , item()*) as item()* := fn:op(",")  )as map(*)

@ChristianGruen
Copy link
Contributor

To be considered before finalizing the proposal (#203): Martin Honnen’s considerations on letting $key return a sequence of keys, as discussed on the mailing list.

@ndw
Copy link
Contributor

ndw commented Oct 18, 2022

Closed by #203

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature A change that introduces a new feature XQFO An issue related to Functions and Operators
Projects
None yet
Development

No branches or pull requests

4 participants