I have another little problem with xQuery/xPath. For my homework i need to output the product range by branch and product category, where the output should only contain those branches (filiale), that have products (produkt and prodInSortiment) of every category (kategorie).
For the XML data please see the URL provided in the code.
While this gives me the right amount of products for each branch, i need to group it further by product category:
declare context item := doc("https://etutor.dke.uni-linz.ac.at/etutor/XML?id=1");
let $filialen := //filiale,
$sortiment := //prodInsortiment,
$produkte := //produkt,
$kategorien := distinct-values(//produkt/kategorie)
for $f in $filialen
let $s := $f//prodInSortiment
let $scat := distinct-values($produkte[#ean = $s/ean]/kategorie)
let $filsort := $produkte[#ean = $s/ean]/#ean
let $catcount := count($scat)
where $catcount = count(distinct-values($kategorien))
return
<filiale filialeNr="{$f/#filNr}">
{for $fs in $filsort return <ean>{$fs}</ean>
}
</filiale>
This is my approach of grouping it further, but it returns every product for every category(kategorie) in every branch(filiale):
declare context item := doc("https://etutor.dke.uni-linz.ac.at/etutor/XML?id=1");
let $filialen := //filiale,
$sortiment := //prodInsortiment,
$produkte := //produkt,
$kategorien := distinct-values(//produkt/kategorie)
for $f in $filialen
let $s := $f//prodInSortiment
let $scat := distinct-values($produkte[#ean = $s/ean]/kategorie)
let $filsort := $produkte[#ean = $s/ean]/#ean
let $catcount := count($scat)
where $catcount = count(distinct-values($kategorien))
return
<filiale filialeNr="{$f/#filNr}">
{for $cat in $scat return
<prodGruppe val = "{$cat}">
{for $fs in $filsort return <ean>{$fs}</ean>
}
</prodGruppe>
}
</filiale>
The result should look like this (this is the correct output for filiale "1"):
<filiale filialeNr="1">
<prodGruppe val="Pflege">
<ean>0-666-4567-2-22</ean>
<ean>0-777-4997-2-43</ean>
<ean>0-456-4887-3-22</ean>
<ean>0-55-48567-16-2</ean>
</prodGruppe>
<prodGruppe val="Ersatz">
<ean>1-626-7767-2-99</ean>
<ean>1-256-7700-2-00</ean>
<ean>1-333-7788-2-31</ean>
<ean>2-446-7240-9-15</ean>
<ean>9-396-7510-9-00</ean>
</prodGruppe>
<prodGruppe val="Audio">
<ean>7-2881-760-3-70</ean>
<ean>5-2671-955-5-55</ean>
<ean>1-4444-652-8-88</ean>
<ean>3-1111-654-3-99</ean>
</prodGruppe>
<prodGruppe val="Sonstiges">
<ean>6-581-1766-3-45</ean>
<ean>6-231-4777-3-15</ean>
<ean>4-1161-730-3-88</ean>
<ean>0-4381-880-7-00</ean>
<ean>5-6661-000-0-00</ean>
</prodGruppe>
Since there is an upcoming mid-term exam, I'm not just asking for a solution, I'm also interested in possible simpler ways to achieve the correct output.
Thanks in advance for any helpful information!
Assuming you have XQuery 3's group-by clause (https://www.w3.org/TR/xquery-31/#id-group-by) you can rewrite the last return to
return
<filiale filialeNr="{$f/#filNr}">
{
for $prod in $s
group by $cat := $produkte[#ean = $prod/ean]/kategorie
return
<prodGruppe val = "{$cat}">
{
$prod/ean
}
</prodGruppe>
}
</filiale>
I think.
I have problem how to print a variable for, in outside for, in Go?
I'm using library GJSON gjson
I have try many way , I just entered the global variable but just appear final index,
like:
datePriodGlobal = DatePeriod.String()
and
datePriodGlobal = DatePeriod.String()
another way I try but appear just final index too, like below:
tempPayments:= "Envelope.Body.GetCustomReportResponse.GetCustomReportResult.ContractSummary.PaymentCalendarList.PaymentCalendar."
resultMapPriodTest := gjson.Get(jsonString,tempPayments + "#.Date")
resultContractsSubmittedTest := gjson.Get(jsonString, tempPayments + "#.ContractsSubmitted")
var datePriodGlobal string
for _, DatePeriod := range resultMapPriodTest.Array()[1:13] {
datePriodGlobal = fmt.Sprintf("%s", DatePeriod.String())
}
var contractsSubmittedGlobal string
for _, ContractsSubmitted := range resultContractsSubmittedTest.Array()[1:13]{
contractsSubmittedGlobal = fmt.Sprintf("%s", ContractsSubmitted.String())
}
fmt.Printf("%s | %s \t|",datePriodGlobal, contractsSubmittedGlobal)
}
I have json like this:
I will suggest just iterate over the PaymentCalendar as a slice of JSON objects rather than querying each field using the indexes as their pseudo-ids.
Here is a simple demonstration:
func main() {
jsonString := `
{
"PaymentCalendarList": {
"PaymentCalendar": [
{"ContractSubmitted": 10,
"Date": "2018-01-01T01:01:01"},
{"ContractSubmitted": 20,
"Date": "2018-01-01T02:02:02"},
{"ContractSubmitted": 30,
"Date": "2018-01-01T03:03:03"}
{"ContractSubmitted": 40,
"Date": "2018-01-01T04:04:04"}
{"ContractSubmitted": 50,
"Date": "2018-01-01T05:05:05"}
]
}
}`
result := gjson.Get(jsonString, "PaymentCalendarList.PaymentCalendar")
for _, paymentCal := range result.Array()[0:3] {
date := paymentCal.Get("Date")
contractSubmit := paymentCal.Get("ContractSubmitted")
fmt.Printf("%s | %s\n", date, contractSubmit)
}
}
Playground
"Cannot use 'DatePeriod' (type Result) as type string in assignment"
So, the variable DatePeriod is a Result type, not a String. You're specifying you want to print a string with %s, but not giving fmt.Sprintf a string, causing that error. The Sprintf is unnecessary if the value given was already a String.
Looking at gjson.go, the Result type has a String() method, so you'd want instead DatePeriod.String().
EDIT:
From your latest edit, I think I see your second issue. Your loops replace the ...Global string variables each time, so you'll only ever get the last value in the slice you've passed to range. Since your slices are identical in length, you might be better off with something like this:
resultMapPriodTest := gjson.Get(jsonString,tempPayments + "#.Date")
resultContractsSubmittedTest := gjson.Get(jsonString, tempPayments + "#.ContractsSubmitted")
dateArray := resultMapPriodTest.Array()[1:13]
contractsArray := resultContractsSubmittedTest.Array()[1:13]
for i := 0; i<len(dateArray); i++ {
d := dateArray[i].String()
c := contractsArray[i].String()
fmt.Printf("%s | %s \t|", d, c)
}
Hi I'm working on a function to format values in currency. I'm using golang.org/x/text/currency for the job, but I'm getting the output with points at the place of commas and no thousands separators.
func (produto *Produto) FormataPreco(valor int64) string {
unit, _ := currency.ParseISO("BRL")
p := message.NewPrinter(language.BrazilianPortuguese)
return p.Sprint(currency.Symbol(unit.Amount(float64(valor) / 100)))
}
The expected result should be R$ 123.456,78 but I'm getting R$ 123456.78
--- Edit ---
I did a version using hardcoded values, but I would like a solution that uses system locale resources.
func (produto *Produto) FormataPreco(valor int64) string {
p := message.NewPrinter(language.BrazilianPortuguese)
return p.Sprintf("R$ %.2f", float64(valor/100))
}
In this example, I inferred the currency format from the language code.
https://goplay.space/#fqs9t8MG062
n := display.Tags(language.English)
for _, lcode := range []string{"en_US", "pt_BR", "de", "ja", "hi"} {
lang := language.MustParse(lcode)
cur, _ := currency.FromTag(lang)
scale, _ := currency.Cash.Rounding(cur) // fractional digits
dec := number.Decimal(100000.00, number.Scale(scale))
p := message.NewPrinter(lang)
p.Printf("%24v (%v): %v%v\n", n.Name(lang), cur, currency.Symbol(cur), dec)
}
// American English (USD): $100,000.00
// Brazilian Portuguese (BRL): R$100.000,00
// German (EUR): €100.000,00
// Japanese (JPY): ¥100,000
// Hindi (INR): ₹1,00,000.00
You could also parse ISO currency codes, but then you must also specify the language in which to format the number. The output language will not affect the number of fractional digits, but it will affect where commas and periods are used:
https://goplay.space/#DlxSmjZbHH6
for _, iso := range []string{"USD", "BRL", "EUR", "JPY", "INR"} {
cur := currency.MustParseISO(iso)
scale, _ := currency.Cash.Rounding(cur) // fractional digits
dec := number.Decimal(100000.00, number.Scale(scale))
p := message.NewPrinter(language.English)
p.Printf("%v: %v%v\n", cur, currency.Symbol(cur), dec)
}
// USD: $100,000.00
// BRL: R$100,000.00
// EUR: €100,000.00
// JPY: ¥100,000
// INR: ₹100,000.00
Certain currencies are rounded in increments, like 0.05 or 0.50. For those cases, the second return value of currency.Cash.Rounding(cur) will return 5 or 50 instead of 1. To give the Decimal formatter the IncrementString it expects, we have to do a little more processing:
package main
import (
"math"
"strconv"
"golang.org/x/text/currency"
"golang.org/x/text/language"
"golang.org/x/text/language/display"
"golang.org/x/text/message"
"golang.org/x/text/number"
)
func main() {
n := display.Tags(language.English)
for _, lcode := range []string{"en_US", "en_CA", "da", "ja"} {
lang := language.MustParse(lcode)
cur, _ := currency.FromTag(lang)
scale, incCents := currency.Cash.Rounding(cur) // fractional digits
incFloat := math.Pow10(-scale) * float64(incCents)
incFmt := strconv.FormatFloat(incFloat, 'f', scale, 64)
dec := number.Decimal(100000.26,
number.Scale(scale), number.IncrementString(incFmt))
p := message.NewPrinter(lang)
p.Printf("%24v %v, %4s-rounding: %3v%v\n",
n.Name(lang), cur, incFmt, currency.Symbol(cur), dec)
}
}
// American English USD, 0.01-rounding: $100,000.26
// Canadian English CAD, 0.05-rounding: CA$100,000.25
// Danish DKK, 0.50-rounding: DKK100.000,50
// Japanese JPY, 1-rounding: ¥100,000
As you may notice, the golang.org/x/text/currency is still a work in progress and hasn't been updated in a while.
NOTE: the formatting functionality is currently under development and
may change without notice.
If you prefer a simple helper function to get the job done, you can use something like this:
func formatMoney(value int32, thousand, decimal string) string {
var result string
var isNegative bool
if value < 0 {
value = value * -1
isNegative = true
}
// apply the decimal separator
result = fmt.Sprintf("%s%02d%s", decimal, value%100, result)
value /= 100
// for each 3 dígits put a dot "."
for value >= 1000 {
result = fmt.Sprintf("%s%03d%s", thousand, value%1000, result)
value /= 1000
}
if isNegative {
return fmt.Sprintf("$ -%d%s", value, result)
}
return fmt.Sprintf("$ %d%s", value, result)
}
usage:
formatMoney(10, ",", ".") // $ 0.10
formatMoney(12345, ",", ".") // $ 123.45
formatMoney(1234567, ",", ".") // $ 12,345.67
formatMoney(-1234567, ",", ".") // $ -12,345.67
I have the following function
declare private function local:get-map() as map:map*
{
let $map := map:map()
for $chart in xdmp:directory("/charts/")
for $panel in $chart/delphi:chart/delphi:panels/delphi:panel
for $dataline in $panel/delphi:datalines/delphi:dataline
let $datasetHref := $dataline/delphi:link[#rel="dataset"]/#href
let $axisId := $dataline/delphi:dimensions/delphi:dimension[#field="y"]/#axis
let $label := $panel/delphi:axes[#dimension="y"]/delphi:axis[#id=$axisId]/#label
let $l := map:get ($map, $datasetHref)
let $updateMap := if (fn:exists ($l)) then () else map:put ($map, $datasetHref, $label)
return $map
};
I have been forced to declare the return type as map:map* because for some reason $map is an array of maps rather than a map. The array contains many items, where each item contains the same map that I need. So when I call this method I use take the first item. Problem is this is not exactly elegant. What I don't understand is why do I get multiple copies of the same map in an array. I expected the code to return a single map. How do I rewrite this to solve the issue?
It's returning a sequence of maps, because each iteration of each of the "for"s returns it. Try this:
declare private function local:get-map() as map:map
{
let $map := map:map()
let $populate :=
for $chart in xdmp:directory("/charts/")
for $panel in $chart/delphi:chart/delphi:panels/delphi:panel
for $dataline in $panel/delphi:datalines/delphi:dataline
let $datasetHref := $dataline/delphi:link[#rel="dataset"]/#href
let $axisId := $dataline/delphi:dimensions/delphi:dimension[#field="y"]/#axis
let $label := $panel/delphi:axes[#dimension="y"]/delphi:axis[#id=$axisId]/#label
let $l := map:get ($map, $datasetHref)
return if (fn:exists ($l)) then () else map:put ($map, $datasetHref, $label)
return $map
};
This does the FLWOR statement in its own let, then returns the map.
I have a string and I want to use it as a selector in xpath to select a node with name as value of the string.
declare variable $get_count := <count><comedy>1</comedy></count>;
(: $string = "comedy" :)
let $value = $get_count/$string (: but this doesn't return anything)
How shall i do it?
let $value = $get_count/$string (: but this doesn't return anything)
Use:
declare variable $get_count := <count><comedy>1</comedy></count>;
declare variable $string := "comedy";
let $value := $get_count/*[name()=$string]
return
$value
When this is applied on any XML document (not used), the wanted, correct result is produced:
<comedy>1</comedy>