Why does $map not output an array in case the input array is one in length? - jsonata

Please check this - https://try.jsonata.org/0L_oYffzT
Here the output of the $map function is not an array.
whereas https://try.jsonata.org/Rf8UI_TMy seems to work fine when the input array is more than 1 in length.

You may use the result structure feature to accomplish this task:
var json = { "projectIds": [ 1 ] }
var result = jsonata('projectIds[].{"projectId": $}').evaluate(json);
console.log(JSON.stringify(result))
<script src="https://cdn.jsdelivr.net/npm/jsonata/jsonata.min.js"></script>
Using $map for this purpose has an open issue that point to workarounds only

Related

XQuery - How can I count and save informations in a map

I want to safe IDs in a map. If the ID occurs again, I want to set the count ($value) from 1 to 2 and so on.
Following you find my code:
declare namespace functx = "http://www.functx.com";
declare variable $idMap := map{};
declare function functx:uniqueID ($entityID as xs:string) as xs:integer {
let $idMap := map:merge(($idMap, if(not(map:contains($idMap, $entityID))) then map:entry($entityID, 1) else map:entry(entityID, map:get($idMap, $entityID)+1)))
return map:get($idMap, $entityID)
};
declare variable $map := map:merge((
map:entry("Sheff", "85246525"),
map:entry("Peter", "85246454"),
map:entry("Marcel", "85246525"),
map:entry("Lion", "85244565"),
map:entry("Klaus", "85241234")
));
map:for-each($map,
function($key, $value) {
functx:uniqueID($value)
}
)
Result:
1
1
1
1
1
Expected Result
1
1
2 (: Because it is the second time, that 85246525 occurs. :)
1
1
Edited 23.03.2020 - 17:45:
I have a complex xquery, which functions. But the target system need unique IDs per line. I have a map, which hold my information like the upper one. I need to add something behind the IDs like (001, 002, 003) to have different IDs.
Best practice would be, that only douplicate IDs get a added number.
Do you understand or what do you need more from me?
One way to construct a new map with an "index" added to duplicated values is to use grouping:
map:merge(
for $key in map:keys($map)
group by $value := $map($key)
for $group-key at $pos in $key
return map:entry($group-key, $value || '-' || format-integer($pos, '000'))
)
At https://xqueryfiddle.liberty-development.net/6qVSgeT that gives
{
"Peter": "85246454-001",
"Marcel": "85246525-001",
"Sheff": "85246525-002",
"Lion": "85244565-001",
"Klaus": "85241234-001"
}
You've written your code as if $idMap is mutable, and as if calling functx:uniqueID() has the side-effect of modifying the map. That isn't going to work in a functional language.
You need a completely different approach; and to help you with that, we need to look at the problem you are trying to solve, not at your existing approach to a solution.

Lodash sortedby object list is stuck in _wrapper_

I am experimenting with lodash sorting. I got the lodash to sort the object list although the sorted results are stuck in wrapper. If I use .value(), then I get unsorted keys output.
var testData = {c:1,b:2,a:3}
var sortedKeys = _(testData).keys().sortBy(key => {return testData.key});
console.log(sortedKeys);
will produce:
LodashWrapper {__wrapped__: {…}, __actions__: Array(2), __chain__: false, __index__: 0, __values__: undefined}
__actions__:(2) [{…}, {…}]
__chain__:false
__index__:0
__values__:undefined
__wrapped__:
a:3
b:2
c:1
__proto__:Object
__proto__:lodash
What is it that I am missing in order to get sorted object list out of lodash wrapper.
When you do testData.key, I'm pretty confident that you actually mean to be doing testData[key].
That alone allows the method to work properly i.e. return an array of Object keys sorted by values. Note that you still have to call .value() if you'd like to unwrap the lodash object.
If there's something else you're expecting, please clarify.
const testData = {c:1,b:2,a:0}
const sortedKeys = _(testData).keys().sortBy(key => {return testData[key]})
/* can do without the return like the below as well */
// const sortedKeys = _(testData).keys().sortBy(key => testData[key])
console.log(sortedKeys.value())
// produces ['a','c','b']
If you want the key and value pair, try the below.
_(obj).toPairs().sortBy(0).value()
There are few things that are happening here which I think are important to note:
First you are starting your sorting statement with the short notation for the lodash _.chain method which allows the results of one operation to flow into the next one. This is similar to how _.flow works in lodash/fp.
Chaining requires the last operation in the chain to end with values() in order to get your actual result from the lodash wrapper. So if you did:
_(testData).keys().sortBy(key => {return testData.key}).values(); // OR
_.chian(testData).keys().sortBy(key => {return testData.key}).values();
You would get some results.
Second issue is that in your flow you get the keys of the objects but then you are not actually sorting by them. To do this you need something like this:
var testData = {c:1, b:2, a:3}
console.log(_.chain(testData).keys().sortBy().value());
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
The difference here is in the sort function where you already have the keys as `[ 'c', 'b', 'a' ] so you just need to sort them. They are not objects anymore but simply strings.
Hope this helps.

How to use ForEach controller on an array?

jmeter ForEach controller can be used to iterate over variables with same prefix like,
myVar_1
myVar_2
myVar_3
But in my case input variable is array of strings, [ "val1", "val2", "val3" ] How to iterate over an array and send separate request for each value?
You won't be able to feed this JSON Array to the ForEach Controller, but you can convert it into a form which can be understood by the ForEach Controller
Add a JSR223 Sampler after the variable holding this JSON Array is defined
Put the following code into the "Script" area:
def json = new groovy.json.JsonSlurper().parseText(vars.get("yourInputVariable"))
def counter = 1
json.each {
vars.put("myVar_" + counter, it)
counter++
}
Replace yourInputVariable with the actual name of the variable holding the JSON Array
Add ForEach Controller under the JSR223 Sampler and perform "normal" configuration as you would do it for myVar_1, myVar_2,... - it will work fine as JSR223 Sampler creates the relevant variables basing on the data from the JSON Array.
See Parsing and producing JSON - Groovy and Groovy Is the New Black articles for more information.
Same way as you use for same prefixed variables.
For variable myVar
myVar = ["val1", "val2", "val3"];
//Following variables are automatically created
myVar_1 = "val1";
myVar_2 = "val2";
myVar_3 = "val3";
ForEach controller will be used on myVar_1, myVar_2, myVar_3
Use Debug Sampler to ensure.
jmeter version : 3.1 r1770033
I tried Dmitri's answer, but basically got stuck with the groovy script as it works only for a simple array of strings. I, however, needed a more complex array of JSON objects.
Then I switched the scripting language to ecmascript and based on the original script wrote a good old JS like this:
var jsonObject = JSON.parse(vars.get("ReportSources"));
for(var index = 0; index < jsonObject.length; index++) {
vars.put("rs_" + (index + 1), JSON.stringify(jsonObject[index]));
}
It worked for me.

xs:string as element() in XQuery

Let's assume I have a XML files, one like this:
<SampleF>
<FirstNode AAA="Something" BBB="Something"></FirstNode>
<SecondNode CCC="Random" DDD="Random"></SecondNode>
</SampleF>
And second one like this:
<SampleF2>
<FirstNode>
<AAA>Something</AAA>
<BBB>Random</BBB>
</FirstNode>
</SampleF2>
And I would like to obtain from both (AAA="Something"/Something) of them as element(). How do I convert it? When in first case I get xs:string and in the second document-node().
I made something like this for the first example but I'm 100% certain there is a better way of doing this
declare function getElementFirstExample($message) as element() {
let $name := "AAA"
let $value := $message/*:SampleF1/*:FirstNode/#AAA
return element {$name} {"$value"}
};
Thank you in advance for your help and advices.
As I understand, you want the value of <FirstNodes/>s AAA attribute or child element, no matter whether it is in the element or attribute.
Use an alternative axis step for accessing both the attribute and element, and data(...) to retrieve the string value.
data(//FirstNode/(#AAA, AAA))
Putting this together for your function and explicit use case:
declare function getElementFirstExample($message) as element() {
let $name := "AAA"
let $value := data($message/*:SampleF1/*:FirstNode/(#AAA, *:AAA))
return element {$name} {"$value"}
};

Read image IPTC data

I'm having some trouble with reading out the IPTC data of some images, the reason why I want to do this, is because my client has all the keywords already in the IPTC data and doesn't want to re-enter them on the site.
So I created this simple script to read them out:
$size = getimagesize($image, $info);
if(isset($info['APP13'])) {
$iptc = iptcparse($info['APP13']);
print '<pre>';
var_dump($iptc['2#025']);
print '</pre>';
}
This works perfectly in most cases, but it's having trouble with some images.
Notice: Undefined index: 2#025
While I can clearly see the keywords in photoshop.
Are there any decent small libraries that could read the keywords in every image? Or am I doing something wrong here?
I've seen a lot of weird IPTC problems. Could be that you have 2 APP13 segments. I noticed that, for some reasons, some JPEGs have multiple IPTC blocks. It's possibly the problem with using several photo-editing programs or some manual file manipulation.
Could be that PHP is trying to read the empty APP13 or even embedded "thumbnail metadata".
Could be also problem with segments lenght - APP13 or 8BIM have lenght marker bytes that might have wrong values.
Try HEX editor and check the file "manually".
I have found that IPTC is almost always embedded as xml using the XMP format, and is often not in the APP13 slot. You can sometimes get the IPTC info by using iptcparse($info['APP1']), but the most reliable way to get it without a third party library is to simply search through the image file from the relevant xml string (I got this from another answer, but I haven't been able to find it, otherwise I would link!):
The xml for the keywords always has the form "<dc:subject>...<rdf:Seq><rdf:li>Keyword 1</rdf:li><rdf:li>Keyword 2</rdf:li>...<rdf:li>Keyword N</rdf:li></rdf:Seq>...</dc:subject>"
So you can just get the file as a string using file_get_contents(get_attached_file($attachment_id)), use strpos() to find each opening (<rdf:li>) and closing (</rdf:li>) XML tag, and grab the keyword between them using substr().
The following snippet works for all jpegs I have tested it on. It will fill the array $keys with IPTC tags taken from an image on wordpress with id $attachment_id:
$content = file_get_contents(get_attached_file($attachment_id));
// Look for xmp data: xml tag "dc:subject" is where keywords are stored
$xmp_data_start = strpos($content, '<dc:subject>') + 12;
// Only proceed if able to find dc:subject tag
if ($xmp_data_start != FALSE) {
$xmp_data_end = strpos($content, '</dc:subject>');
$xmp_data_length = $xmp_data_end - $xmp_data_start;
$xmp_data = substr($content, $xmp_data_start, $xmp_data_length);
// Look for tag "rdf:Seq" where individual keywords are listed
$key_data_start = strpos($xmp_data, '<rdf:Seq>') + 9;
// Only proceed if able to find rdf:Seq tag
if ($key_data_start != FALSE) {
$key_data_end = strpos($xmp_data, '</rdf:Seq>');
$key_data_length = $key_data_end - $key_data_start;
$key_data = substr($xmp_data, $key_data_start, $key_data_length);
// $ctr will track position of each <rdf:li> tag, starting with first
$ctr = strpos($key_data, '<rdf:li>');
// Initialize empty array to store keywords
$keys = Array();
// While loop stores each keyword and searches for next xml keyword tag
while($ctr != FALSE && $ctr < $key_data_length) {
// Skip past the tag to get the keyword itself
$key_begin = $ctr + 8;
// Keyword ends where closing tag begins
$key_end = strpos($key_data, '</rdf:li>', $key_begin);
// Make sure keyword has a closing tag
if ($key_end == FALSE) break;
// Make sure keyword is not too long (not sure what WP can handle)
$key_length = $key_end - $key_begin;
$key_length = (100 < $key_length ? 100 : $key_length);
// Add keyword to keyword array
array_push($keys, substr($key_data, $key_begin, $key_length));
// Find next keyword open tag
$ctr = strpos($key_data, '<rdf:li>', $key_end);
}
}
}
I have this implemented in a plugin to put IPTC keywords into WP's "Description" field, which you can find here.
ExifTool is very robust if you can shell out to that (from PHP it looks like?)

Resources