XQuery 3: Conditionally add map entry - saxon

Example XQuery (using Saxon-HE, version 9.8.0.6)
xquery version "3.1";
let $xml := <simple>
<hello>Hello World!</hello>
</simple>
return fn:serialize(map{
'greeting': data($xml/hello),
'number': data($xml/number) (: ? how to add this entry only if there is a number ? :)
}, map{'method':'json', 'indent':true()})
Output:
{
"number":null,
"greeting":"Hello World!"
}
Question
How to prevent entries with a null value (in this case 'number')? Or more specifically in this case: how to add the 'number' entry only if it is a number?
Note: I know about map:entry and map:merge. I am looking for a solution without these functions, so "inline" (within the map constructor).
Update
Based on the answer of #joewiz, this is not possible. This is the closest we can get:
xquery version "3.1";
declare namespace map="http://www.w3.org/2005/xpath-functions/map";
let $xml := <simple>
<hello>Hello World!</hello>
</simple>
return fn:serialize(
map:merge((
map:entry('greeting', data($xml/hello)),
let $n := number($xml/number) return if ($n) then map:entry('number', $n) else()
)),
map{'method':'json', 'indent':true()})

If you're doing this often enough to make it worthwile you could do:
declare function f:addConditionally($map, $key, $data) as map(*) {
if (exists($data)) then map:put($map, $key, $data) else $map
};
let $m :=
map{}
=> f:addConditionally('greeting', data($xml/hello))
=> f:addConditionally('number', data($xml/number))

According to the XQuery 3.1 specification on Map Constructors, map constructors consist of map constructor entries, which themselves consist of a map key expression and a map value expression. In other words, map constructor entries are not general expressions and cannot accommodate a conditional expression such as:
map { if ($condition) then "key": "value" else () }
If you need to put anything in a map besides a key-value expression pair, you'll need to abandon the map constructor and fall back on map:merge() and map:entry(). The correct syntax for the above case would be as follows:
map:merge( if ($condition) then map:entry("key", "value") else () )

Related

Dart firstWhere orElse: generic function return type

The code below defines a generic myFirstWhereFunction with 3 arguments:
Generic list
Generic value to search in the list
Generic default value to return if the searched value is not in the passed generic list
The code:
void main() {
const List<int> intLst = [1, 2, 3, 4];
print(myFirstWhereFunc(intLst, 4, -1));
print(myFirstWhereFunc(intLst, 5, -1));
const List<String> strLst = ['coucou', 'go', 'bold', 'tooltip'];
print(myFirstWhereFunc(strLst, 'go', 'not exist'));
print(myFirstWhereFunc(strLst, 'ok', 'not exist'));
}
T myFirstWhereFunc<T>(List<T> lst, T searchVal, T defaultVal) {
return lst.firstWhere((element) => element == searchVal, orElse: <T> () {
return defaultVal;
});
}
But this code generates an exception.
One solution is to replace the generic myFirstWhereFunc return type by dynamic (code below):
dynamic myFirstWhereFunc<T>(List<T> lst, T searchVal, T defaultVal) {
return lst.firstWhere((element) => element == searchVal,
orElse: () => defaultVal);
}
But is there another way of solving the problem ?
I believe that the problem is that when you do:
print(myFirstWhereFunc(intLst, 4, -1));
there are two possible ways to infer the type of myFirstWhereFunc:
Bottom-up (inside-out): myFirstWhereFunc is called with a List<int> and with int arguments, so its type could be myFirstWhereFunc<int>. This is what you want.
Top-down (outside-in): print has an Object? parameter, so myFirstWhereFunc could be myFirstWhereFunc<Object?> so that it returns an Object?. This is what actually happens and is what you do not want.
Dart ends up with two possible ways to infer the generic type parameter, both seem equally valid at compilation-time, and it picks the one that you happen to not want. Picking the other approach likely would result in undesirable outcomes for different code examples. Arguably inference could try both ways and pick the narrower type if one approach leads to a subtype of the other. I'm not sure offhand if that would break code, but I wouldn't be surprised. (I also suspect that it's been suggested in https://github.com/dart-lang/language/issues somewhere...)
Changing myFirstWhereFunc's return type to dynamic is a crude workaround to the problem because it makes myFirsyWhereFunc's type no longer inferrable from print's parameter type.
If you split the line up and use a temporary variable, inference can infer myFirstWhereFunc independently of print and then should do what you want:
var intResult = myFirstWhereFunc(intLst, 4, -1);
print(intResult);

How to convert an Iterable of type X to type Y Re: Iterable<WordPair> to Iterable<String>

In pubspec.yaml, I'm using english_words library to generate wordpairs:
dependencies:
flutter:
sdk: flutter
# Contains a few thousand of the most used English words
# plus some utility functions.
english_words: ^3.1.0
Now the WordPair Class is not a subtype of String and so I can't use the Iterable's lambdas or functions like cast or retype to 'cast' the 'WordPairs' to Strings.
So, I had to write the function called getWords().
See below the Dart file, Model.dart, that contains this implementation.
You'll see the old line commented out where it was returning in the getter the type Iterable.
Would there be a more efficient way to do this?
For example, I didn't want to involve a List Class in the conversion, but I can't find any other way to successfully do this.
Thanks.
---------------- Model.dart
import 'package:english_words/english_words.dart' show WordPair, generateWordPairs;
import 'dart:collection';
/// Model Class
///
class Model {
String get randomWordPair => new WordPair.random().asPascalCase;
// Iterable<WordPair> get wordPairs => generateWordPairs().take(10);
Iterable<String> get wordPairs => getWords();
Iterable<String> getWords(){
Iterable<WordPair> pairs = generateWordPairs().take(10);
ListWords<String> words = new ListWords();
for (var pair in pairs) {
words.add(pair.asString);
}
return words;
}
}
class ListWords<E> extends ListBase<E> {
final List<E> l = [];
set length(int newLength) { l.length = newLength; }
int get length => l.length;
E operator [](int index) => l[index];
void operator []=(int index, E value) { l[index] = value; }
}
In Dart 2 you can use
iterable.cast<NewType>()
but it is prone to lead to inefficiency if the resulting list is accessed often, because it wraps the original iterable into a new one and has to forward every access.
Usually more efficient are
new List<NewType>.of(oldList)
or
new List.from<NewType.from(oldList)
I was not able to derive the difference between .of() and from() from the docs though (https://api.dartlang.org/dev/2.0.0-dev.50.0/dart-core/List/List.from.html, https://api.dartlang.org/dev/2.0.0-dev.50.0/dart-core/List/List.of.html)
At first glance, a loop that is collecting the result of an expression can generally be replaced with an appropriate .map method invocation on an Iterable. See if that will help.

Validator\Db\RecordExists with multiple columns

ZF2 docs show the following example in terms of using Db\RecordExists validator with multiple columns.
$email = 'user#example.com';
$clause = $dbAdapter->quoteIdentifier('email') . ' = ' . $dbAdapter->quoteValue($email);
$validator = new Zend\Validator\Db\RecordExists(
array(
'table' => 'users',
'field' => 'username',
'adapter' => $dbAdapter,
'exclude' => $clause
)
);
if ($validator->isValid($username)) {
// username appears to be valid
} else {
// username is invalid; print the reason
$messages = $validator->getMessages();
foreach ($messages as $message) {
echo "$message\n";
}
}
I’ve tried this using my own Select object containing a more complex where condition. However, isValid() must be called with a value parameter.
In the example above $username is passed to isValid(). But there seems to be no according field definition.
I tried calling isValid() with an empty string, but this does not produce the desired result, since Zend\Validator\Db\AbstractDb::query() always adds the value to the statement:
$parameters = $statement->getParameterContainer();
$parameters['where1'] = $value;
If I remove the seconds line above, my validator produces the expected results.
Can someone elaborate on how to use RecordExists with the where conditions in my custom Select object? And only those?
The best way to do this is probably by making your own validator that extends one of Zend Framework's, because it doesn't seem like the (No)RecordExists classes were meant to handle multiple fields (I'd be happy to be proven wrong, because it'd be easier if they did).
Since, as you discovered, $parameters['where1'] is overridden with $value, you can deal with this by making sure $value represents what the value of the first where should be. In the case of using a custom $select, $value will replace the value in the first where clause.
Here's a hacky example of using RecordExists with a custom select and multiple where conditions:
$select = new Select();
$select->from('some_table')
->where->equalTo('first_field', 'value1') // this gets overridden
->and->equalTo('second_field', 'value2')
;
$validator = new RecordExists($select);
$validator->setAdapter($someAdapter);
// this overrides value1, but since isValid requires a string,
// the redundantly supplied value allows it to work as expected
$validator->isValid('value1');
The above produces the following query:
SELECT `some_table`.* FROM `some_table` WHERE `first_field` = 'value1' AND `second_field` = 'value2'
...which results in isValid returning true if there was a result.

IronScheme: How do you 'or' enumeration entries

How do you 'or' enum entries in IronScheme, ex:
(import
(rnrs)
(ironscheme clr))
(clr-using System.Reflection)
(define public-class-attributes
(bitwise-ior
(clr-static-field-get
TypeAttributes Public)
(clr-static-field-get
TypeAttributes Class)))
(display public-class-attributes)
This causes an error, I haven't found an alternative in the documentation.
I am not sure what your use case is, but as mentioned in the comment, when using clr-call a list of symbols can be used for an OR'd enum. Example here.
Internally, the compiler will wrap the list with a call to Helpers.SymbolToEnum<T>().
Note: The case is ignored.
To illustrate in a small example:
C# code:
[Flags]
enum Foo
{
Bar = 1,
Baz = 2
}
class AType
{
static void Kazaam(Foo foo) { ... }
}
Scheme code:
; same as Bar | Baz
(clr-static-call AType Kazaam '(Bar Baz))
; single value
(clr-static-call AType Kazaam 'Baz)
; same thing
(clr-static-call AType Kazaam '(Baz))
; no value (iow zero)
(clr-static-call AType Kazaam '())
If these are just simple flag, lists should suffice, else you can redefine the enum as an enum-set in Scheme which allows many set operations. Finally, you just use enum-set->list to get the list to pass as an argument as shown above.

what is a good way to test parsed json maps for equality?

The following code prints:
false
false
true
{{a: b}, {a: b}}
code
import "dart:json" as JSON;
main() {
print(JSON.parse('{ "a" : "b" }') == JSON.parse('{ "a" : "b" }'));
print({ "a" : "b" } == { "a" : "b" });
print({ "a" : "b" }.toString() == { "a" : "b" }.toString());
Set s = new Set();
s.add(JSON.parse('{ "a" : "b" }'));
s.add(JSON.parse('{ "a" : "b" }'));
print(s);
}
I am using json and parsing two equivalent objects, storing them in a Set, hoping they will not be duplicated. This is not the case and it seems to be because the first two lines (unexpectedly?) results in false. What is an efficient way to correctly compare two Map objects assuming each were the result of JSON.parse()?
The recommended way to compare JSON maps or lists, possibly nested, for equality is by using the Equality classes from the following package
import 'package:collection/collection.dart';
E.g.,
Function eq = const DeepCollectionEquality().equals;
var json1 = JSON.parse('{ "a" : 1, "b" : 2 }');
var json2 = JSON.parse('{ "b" : 2, "a" : 1 }');
print(eq(json1, json2)); // => true
For details see this answer which talks about some of the different equality classes: How can I compare Lists for equality in Dart?.
This is a difficult one, because JSON objects are just Lists and Maps of num, String, bool and Null. Testing Maps and Lists on equality is still an issue in Dart, see https://code.google.com/p/dart/issues/detail?id=2217
UPDATE
This answer is not valid anymore, see answer #Patrice_Chalin
This is actually pretty hard, as the == operator on Maps and Lists doesn't really compare keys/values/elements to each other.
Depending on your use case, you may have to write a utility method. I once wrote this quick and dirty function:
bool mapsEqual(Map m1, Map m2) {
Iterable k1 = m1.keys;
Iterable k2 = m2.keys;
// Compare m1 to m2
if(k1.length!=k2.length) return false;
for(dynamic o in k1) {
if(!k2.contains(o)) return false;
if(m1[o] is Map) {
if(!(m2[o] is Map)) return false;
if(!mapsEqual(m1[o], m2[o])) return false;
} else {
if(m1[o] != m2[o]) return false;
}
}
return true;
}
Please note that while it handles nested JSON objects, it will always return false as soon as nested lists are involved. If you want to use this approach, you may need to add code for handling this.
Another approach I once started was to write wrappers for Map and List (implementing Map/List to use it normally) and override operator==, then use JsonParser and JsonListener to parse JSON strings using those wrappers. As I abandoned that pretty soon, I don't have code for it and don't know if it really would have worked, but it could be worth a try.
The matcher library, used from unittest, will do this.

Resources