How to sort a list of text+date strings in Groovy - sorting

I have a list of strings, each one contains text with date like this:
"foo_6.7.2016"
"foo_5.10.2016"
"foo_6.30.2016"
"foo_6.23.2016"
"foo_6.2.2016"
"foo_5.22.2016"
I need to sort them by Date and get this:
"foo_6.30.2016"
"foo_6.23.2016"
"foo_6.7.2016"
"foo_6.2.2016"
"foo_5.22.2016"
"foo_5.10.2016"

An alternative might be:
def foos = [
"foo_6.7.2016",
"foo_5.10.2016",
"foo_6.30.2016",
"foo_6.23.2016",
"foo_6.2.2016",
"foo_5.22.2016"
]
def sorted = foos.sort(false) { Date.parse('M.d.yyyy', it - 'foo_') }.reverse()

For a quick answer that needs substantial cleanup:
def dates = [
"foo_6.7.2016"
"foo_5.10.2016"
"foo_6.30.2016"
"foo_6.23.2016"
"foo_6.2.2016"
"foo_5.22.2016"
]
def prefix = "foo_"
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("M.d.yyyy")
def sorted_dates = dates.collect{ sdf.parse(
it, new java.text.ParsePosition(prefix.length()) ) }.sort().reverse()
def newDates = sorted_dates.collect{ "${prefix} + ${sdf.format(it)}"}
println newDates

Related

Get value from xml and calculate

I am getting the amounts from an xml file but I need to sum them to check.
I am using Ruby on rails with the Nokogiri gem
Example from xml file:
<cfdi:Conceptos>
<cfdi:Concepto ClaveProdServ="15101514" NoIdentificacion="PL/762/EXP/ES/2015-16665610" Cantidad="52.967" ClaveUnidad="LTR" Descripcion="MAGNA (LT)" ValorUnitario="16.34" Importe="865.74">
<cfdi:Impuestos>
<cfdi:Traslados>
<cfdi:Traslado Base="842.59" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="134.81"/>
</cfdi:Traslados>
</cfdi:Impuestos>
</cfdi:Concepto>
<cfdi:Concepto ClaveProdServ="15101514" NoIdentificacion="PL/767/EXP/ES/2015-8515840" Cantidad="35.045" ClaveUnidad="LTR" Descripcion="MAGNA (LT)" ValorUnitario="16.34" Importe="572.80">
<cfdi:Impuestos>
<cfdi:Traslados>
<cfdi:Traslado Base="557.49" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="89.20"/>
</cfdi:Traslados>
</cfdi:Impuestos>
</cfdi:Concepto>
<cfdi:Concepto ClaveProdServ="15101514" NoIdentificacion="PL/762/EXP/ES/2015-16665910" Cantidad="21.992" ClaveUnidad="LTR" Descripcion="MAGNA (LT)" ValorUnitario="16.34" Importe="359.45">
<cfdi:Impuestos>
<cfdi:Traslados>
<cfdi:Traslado Base="349.84" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="55.97"/>
</cfdi:Traslados>
</cfdi:Impuestos>
</cfdi:Concepto>
<cfdi:Concepto ClaveProdServ="15101514" NoIdentificacion="PL/762/EXP/ES/2015-16665560" Cantidad="25.002" ClaveUnidad="LTR" Descripcion="MAGNA (LT)" ValorUnitario="16.34" Importe="408.62">
<cfdi:Impuestos>
<cfdi:Traslados>
<cfdi:Traslado Base="397.69" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="63.63"/>
</cfdi:Traslados>
</cfdi:Impuestos>
</cfdi:Concepto>
I managed to obtain all the amounts and taxes with these line of code:
array = []
array_i = []
file = Nokogiri::XML(File.open(params[:consumption][:factura]))
doc_pass = file.xpath("//cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto")
doc_pass.each do |pass|
hash_importe = {}
hash_importe[:total] = pass['Importe']
array << hash_importe
end
doc_pass2 = file.xpath("//cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto/cfdi:Impuestos/cfdi:Traslados/cfdi:Traslado")
doc_pass2.each do |pass2|
hash_impuesto = {}
hash_impuesto[:tax] = pass2['Importe']
array_i << hash_impuesto
end
these are the results I get from the xml file:
(byebug) array
[{:importe=>"865.74"}, {:importe=>"572.80"}, {:importe=>"359.45"}, {:importe=>"408.62"}, {:importe=>"324.48"}, {:importe=>"649.64"}, {:importe=>"823.45"}, {:importe=>"545.15"}, {:importe=>"428.02"}, {:importe=>"527.21"}, {:importe=>"487.67"}, {:importe=>"331.72"}, {:importe=>"511.64"}, {:importe=>"406.67"}, {:importe=>"820.81"}, {:importe=>"1635.54"}, {:importe=>"484.14"}, {:importe=>"564.83"}, {:importe=>"1463.30"}]
(byebug) array_i
[{:importe=>"134.81"}, {:importe=>"89.20"}, {:importe=>"55.97"}, {:importe=>"63.63"}, {:importe=>"50.52"}, {:importe=>"101.18"}, {:importe=>"128.21"}, {:importe=>"84.88"}, {:importe=>"66.73"}, {:importe=>"82.10"}, {:importe=>"75.90"}, {:importe=>"51.58"}, {:importe=>"79.67"}, {:importe=>"63.33"}, {:importe=>"127.80"}, {:importe=>"254.69"}, {:importe=>"75.36"}, {:importe=>"87.92"}, {:importe=>"227.84"}]
now what I want is to sum both values(importe + impuesto) ​​for example:
865.74 + 134.81
572.80 + 89.20
359.45 + 55.97
I am new with rails, I would appreciate your help
You can return an array with results if both arrays have the same size(I think yes), like this:
(0..array.size - 1).each_with_object([]) { |i, obj| obj << array[i][:importe].to_f + array_i[i][:importe].to_f }
result:
[1000.55, 662.0, 415.41999999999996, 472.25, 375.0, 750.8199999999999, 951.6600000000001, 630.03, 494.75, 609.3100000000001, 563.57, 383.3, 591.31, 470.0, 948.6099999999999, 1890.23, 559.5, 652.75, 1691.1399999999999]
Use zip method to combine values at corresponding index of two arrays
result = array.zip(array_i)
.map { |importe, impuesto| importe[:importe].to_f + impuesto[:importe].to_f }
Or can be simplified more for your concrete data structure
result = array.zip(array_i).map { |hashes| hashes.sum {|h| h[:importe].to_f }}
Better approach would be if you extract Concepto object with Impuesto and Importe values directly from xml, then you don't need to combine different arrays, but use nicely structured object.

normalize-space not working on scrapy

I am trying to extract chapter titles and their subtitles from a web page in the url. This is my spider
import scrapy
from ..items import ContentsPageSFBItem
class BasicSpider(scrapy.Spider):
name = "contentspage_sfb"
#allowed_domains = ["web"]
start_urls = [
'https://www.safaribooksonline.com/library/view/shell-programming-in/9780134496696/',
]
def parse(self, response):
item = ContentsPageSFBItem()
item['content_item'] = response.xpath('normalize-space(//ol[#class="detail-toc"]//*/text())').extract();
length = len(response.xpath('//ol[#class="detail-toc"]//*/text()').extract()); #extract()
full_url_list = list();
title_list = list();
for i in range(1,length+1):
full_url_list.append(response.url)
item["full_url"] = full_url_list
title = response.xpath('//title[1]/text()').extract();
for j in range(1,length+1):
title_list.append(title)
item["title"] = title_list
return item
Even though I use the normalise fucntion in my xpath to remove the spaces, I get the following result in my csv
content_item,full_url,title
"
,Chapter 1,
,
,
,Instructor Introduction,
,00:01:00,
,
,
,Course Overview,
How do I get the result with at most only one new line after each entry?
If you want to get all text within Table of Contents section you need to change your xpath expression in item['content_item'] to:
item['content_item'] = response.xpath('//ol[#class="detail-toc"]//a/text()').extract()
You can rewrite you spider code like this:
import scrapy
class BasicSpider(scrapy.Spider):
name = "contentspage_sfb"
start_urls = [
'https://www.safaribooksonline.com/library/view/shell-programming-in/9780134496696/',
]
def parse(self, response):
item = dict() # change dict to your scrapy item
for link in response.xpath('//ol[#class="detail-toc"]//a'):
item['link_text'] = link.xpath('text()').extract_first()
item['link_url'] = response.urljoin(link.xpath('#href').extract_first())
yield item
# Output:
{'link_text': 'About This E-Book', 'link_url': 'https://www.safaribooksonline.com/library/view/shell-programming-in/9780134496696/pref00.html#pref00'}
{'link_text': 'Title Page', 'link_url': 'https://www.safaribooksonline.com/library/view/shell-programming-in/9780134496696/title.html#title'}

How to sort a comma-delimited string?

I have the following Ruby code:
settings= hash.new
settings= batch.getPartialSettings
settings= batchSettings.merge(batch.getEntireSettings)
puts settings
The result is:
{"Resolution"=>"1024", "Applications"=>"Mozilla,IE,Chrome", "Programming"=>"Java,HTML"}
I want "Applications" to be sorted as:
"Applications"=>"Chrome,IE,Mozilla"
So, my final result should be:
{"Resolution"=>"1024", "Applications"=>"Chrome,IE,Mozilla", "Programming"=>"Java,HTML"}
unsorted_apps = settings['Applications']
sorted_apps = unsorted_apps.split(',').sort.join(',')
settings['Applications'] = sorted_apps

How does Xpath in groovy to return Tags and Values

I have the following XML, and in SOAPUI Groovy I am attempting go capture a set of XML with its tags and values, for example:
<telephoneNumbers>
<telephone>
<id>125042741</id>
<areaCode>0161</areaCode>
<phoneNumber>4804420</phoneNumber>
<extension>1234</extension>
<usage>Work</usage>
</telephone>
</telephoneNumbers>
im trying to return to following outcome (tags and values):
<telephone>
<id>125042741</id>
<areaCode>0161</areaCode>
<phoneNumber>4804420</phoneNumber>
<extension>1234</extension>
<usage>Work</usage>
</telephone>
Here is the groovy:
def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context )
def Recall = groovyUtils.getXmlHolder( "Recall#Response" )
def telephone = Recall[ "//telephone//*" ] as String
String returnXml = ""
if ( Recall["//restrict"] != null ) {
returnXml= telephone
}
else
return returnXml
Similar to this answer How do I print a groovy Node with namespace preserved? you could parse the Xml using XmlSlurper and afterwards print the xml nodes you wish to with StreamingMarkupBuilder.
String xml = Recall.getXml()
def telephoneNumbers = new XmlSlurper().parseText(xml)​​​​​​​​
def outputBuilder = new groovy.xml.StreamingMarkupBuilder()
String telephoneXml = outputBuilder.bind { mkp.yield telephoneNumbers.telephone }
I posted a runnable example here: http://groovyconsole.appspot.com/script/1245001 not using SOAPUI but a simple string for demonstration. Hope this helps!

Perl to Ruby conversion (multidimensional arrays)

I'm just trying to get my head around a multidimensional array creation from a perl script i'm currently converting to Ruby, I have 0 experience in Perl, as in i opened my first Perl script this morning.
Here is the original loop:
my $tl = {};
for my $zoom ($zoommin..$zoommax) {
my $txmin = lon2tilex($lonmin, $zoom);
my $txmax = lon2tilex($lonmax, $zoom);
# Note that y=0 is near lat=+85.0511 and y=max is near
# lat=-85.0511, so lat2tiley is monotonically decreasing.
my $tymin = lat2tiley($latmax, $zoom);
my $tymax = lat2tiley($latmin, $zoom);
my $ntx = $txmax - $txmin + 1;
my $nty = $tymax - $tymin + 1;
printf "Schedule %d (%d x %d) tiles for zoom level %d for download ...\n",
$ntx*$nty, $ntx, $nty, $zoom
unless $opt{quiet};
$tl->{$zoom} = [];
for my $tx ($txmin..$txmax) {
for my $ty ($tymin..$tymax) {
push #{$tl->{$zoom}},
{ xyz => [ $tx, $ty, $zoom ] };
}
}
}
and what i have so far in Ruby:
tl = []
for zoom in zoommin..zoommax
txmin = cm.tiles.xtile(lonmin,zoom)
txmax = cm.tiles.xtile(lonmax,zoom)
tymin = cm.tiles.ytile(latmax,zoom)
tymax = cm.tiles.ytile(latmin,zoom)
ntx = txmax - txmin + 1
nty = tymax - tymin + 1
tl[zoom] = []
for tx in txmin..txmax
for ty in tymin..tymax
tl[zoom] << xyz = [tx,ty,zoom]
puts tl
end
end
end
The part i'm unsure of is nested right at the root of the loops, push #{$tl->{$zoom}},{ xyz => [ $tx, $ty, $zoom ] };
I'm sure this will be very simple for a seasoned Perl programmer, thanks! `
The Perl code is building up a complex data structure in $tl -- hash, array, hash, array:
$tl{$zoom}[i]{xyz}[j] = $tx # j = 0
$tl{$zoom}[i]{xyz}[j] = $ty # j = 1
$tl{$zoom}[i]{xyz}[j] = $zoom # j = 2
So I think the key line in your Ruby code should be like this:
tl[zoom] << { 'xzy' => [tx,ty,zoom] }
Note also that the root item ($tl) refers to a hash in the Perl code, while your Ruby code initializes it to be an array. That difference might cause problems for you, depending on the values that $zoom takes.

Resources