Hack iterate (map) over Map - hhvm

I have a Map such as:
$m = Map {
'sort' => 'created',
'order' => 'desc',
}
I want to turn that into a string:
'sort:created order:desc'
I can do this with arrays as explained in this SO answer:
implode(' ', array_map(($k, $v) ==> $k.':'.$v, array_keys($m), $m))
I've read the documentation of Map::items and tried:
$m->items()->map(($key, $value) ==> print($key))
But this prints nothing.
I'm looking for a oneliner like with the arrays, no loops.

map()'s parameter is a function that only takes one argument; if you run the typechecker, it will tell you this:
test.php:9:20,20: Invalid argument (Typing[4039])
/tmp/hh_server/hhi_1ebd4af3/hhi/interfaces.hhi:363:27,44: Number of arguments doesn't match
test.php:9:20,20: Because of this definition
What you want is mapWithKey() https://3v4l.org/GF69D:
$m->mapWithKey(($key, $value) ==> print($key));
You can also use exactly the same code you were using for arrays: https://3v4l.org/mSREI

Use:
implode(' ', $m->mapWithKey(($k, $v) ==> $k.':'.$v))

Related

Dart equivalent to get an element of a sequence and transform it fluently through a pipeline

What is Dart equivalent to get an element of a sequence and transform it with a map() fluently through a pipeline.
Something like doing in Java streams:
var result = ....findFirst().map(elem -> transform(elem)).get();
It does not really make much sense to have a "pipeline" for a single event (if that was what you mean by the findFirst() call). But if you want to, you can combine something with where() and take() so you keep the Iterable and therefore be able to use map on it:
void main() {
print(
[1, 2, 3]
.where((element) => element == 2)
.take(1) // Could also be skipped because of .first later
.map((e) => e.toRadixString(2))
.map((e) => e.padLeft(8, '0'))
.map((e) => 'Number in binary is: $e')
.first,
); // Number in binary is: 00000010
}

Searchkick `all` for multiple arrays with OR

I need to build a boolean query with searchkick which will check multiple arrays and condition must be true if all elements of an array exist.
I wants records which contains ["2019-11-05", "2019-11-06", "2019-11-07"] all dates from one array OR from second array ["2019-11-08", "2019-11-09", "2019-11-10"]
it works perfectly for one array like this.
available_on: { all: ["2019-11-05", "2019-11-06", "2019-11-07"] }
I need something like this
available_on: { or: { all: ["2019-11-05", "2019-11-06", "2019-11-07"] , all: ["2019-11-08", "2019-11-09", "2019-11-10"]} }
how can we create a query available_on = A OR B
A, B are arrays and we need to match all elements of arrays
available_on is target term (also an array in index)
You should use where with or filter like this:
where {or: [[{available_on: {all: ["2019-11-05", "2019-11-06", "2019-11-07"]}},
{available_on: {all: ["2019-11-08", "2019-11-09", "2019-11-10"]}}]]}
or use _or filter:
where {_or: [{available_on: {all: ["2019-11-05", "2019-11-06", "2019-11-07"]}},
available_on: {all: ["2019-11-08", "2019-11-09", "2019-11-10"]}]}
There's no difference of results between _or and or, just a little different in syntax(or use one more pair square brackets)
Have you tried using this inside 'where' clause ?
_or: [{available_on: {all: ["2019-11-05", "2019-11-06", "2019-11-07"]}}, {available_on: {all: ["2019-11-05", "2019-11-06", "2019-11-07"]}} ]}

XQuery 3: Conditionally add map entry

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 () )

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.

Chaining Dart futures - possible to access intermediate results?

Dart allows for chaining futures to invoke more than one async method in sequence without nesting callbacks, which is awesome.
Let's say we want to first connect to a data store like Redis, and then run a bunch of sequential reads:
Future<String> FirstValue(String indexKey)
{
return RedisClient.connect(Config.connectionStringRedis)
.then((RedisClient redisClient) => redisClient.exists(indexKey))
.then((bool exists) => !exists ? null : redisClient.smembers(indexKey))
.then((Set<String> keys) => redisClient.get(keys.first))
.then((String value) => "result: $value");
}
Four async methods and yet the code is fairly easy to read and understand. It almost looks like the steps are executed synchronously and in sequence. Beautiful! (Imagine having to write the same code using nested JavaScript callbacks...)
Unfortunately, this won't quite work: the RedisClient we get from the .connect method is only assigned to a local variable which is not in scope for the subsequent .thens. So, redisClient.smembers and redisClient.get will actually throw a null pointer exception.
The obvious fix is to save the return value in another variable with function scope:
Future<String> FirstValue(String indexKey)
{
RedisClient redisClient = null;
return RedisClient.connect(Config.connectionStringRedis)
.then((RedisClient theRedisClient)
{
redisClient = theRedisClient;
return redisClient.exists(indexKey);
})
.then((bool exists) => !exists ? null : redisClient.smembers(indexKey))
.then((Set<String> keys) => redisClient.get(keys.first))
.then((String value) => "result: $value");
}
Unfortunately, this makes the code more verbose and less beautiful: there's now an additional helper variable (theRedisClient), and we had to replace one of the Lambda expressions with an anonymous function, adding a pair of curly braces and a return statement and another semicolon.
Since this appears to be a common pattern, is there a more elegant way of doing this? Any way to access those earlier intermediate further down the chain?
You can use a nested assignment to avoid curly braces and return :
.then((RedisClient rc) => (redisClient = rc).exists(indexKey))
You can do scopes with futures too, by not putting all the 'then' calls at the same level.
I'd do something like:
Future<String> FirstValue(String indexKey) =>
RedisClient.connect(Config.connectionStringRedis)
.then((RedisClient redisClient) =>
redisClient.exists(indexKey)
.then((bool exists) => !exists ? null : redisClient.smembers(indexKey))
.then((Set<String> keys) => redisClient.get(keys.first))
.then((String value) => "result: $value");
);
Indentation is always difficult with code like this. This example follows the Dart style guide, but I think it could be more readable with less indentation of the then calls:
Future<String> FirstValue(String indexKey) =>
RedisClient.connect(Config.connectionStringRedis)
.then((RedisClient redisClient) =>
redisClient.exists(indexKey)
.then((bool exists) => !exists ? null : redisClient.smembers(indexKey))
.then((Set<String> keys) => redisClient.get(keys.first))
.then((String value) => "result: $value");
);

Resources