Get value from xml and calculate - ruby
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.
Related
how to exclude from counting holidays in rails4
I am trying to make an app for vacation requests but i am facing a problem.I have e model called VacationRequest and a view of VacationRequest where the result will be shown. VacationRequest.rb model def skip_holidays count1 = 0 special_days = Date.parse("2017-05-09", "2017-05-12") special_days.each do |sd| if ((start_data..end_data) === sd) numero = (end_data - start_data).to_i numro1 = numero - sd else numero = (end_data - start_data).to_i end end end VacationRequest show.htm.erb here i called the method from model #vacation_request.skip_holidays this is showing errors and is not working. Please help me with that!
My approach to this would be the following: def skip_holidays special_days = ["2017-05-09", "2017-05-12"].map(&:to_date) accepted_days = [] refused_days = [] (start_data..end_data).each do |requested_date| accepted_days << requested_date unless special_days.include?(requested_date) refused_days << requested_date if special_days.include?(requested_date) end accepted_day_count = accepted_days.count refused_days_count = refused_days.count end This way you iterated over all requested dates (the range), checked if the date is a special day - If so, refuse it, otherwise accept it. In the end you can display statistics about accepted and refused dates.
You cannot create a date range by passing multiple argument to the constructor. Instead call the constructor twice to create a range: special_days = Date.parse("2017-05-09") .. Date.parse("2017-05-12") Or if instead you want only the two dates, do: special_days = ["2017-05-09", "2017-05-12"].map &:to_date
This Date.parse("2017-05-09", "2017-05-12") don`t create a range, only return the first params parsed: #irb Date.parse("2017-05-09", "2017-05-12") => Tue, 09 May 2017 You can do it this way: initial = Date.parse("2017-05-09") final = Date.parse("2017-05-12") ((initial)..final).each do |date| #rules here. end
How to sort a list of text+date strings in Groovy
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
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
sorting a nested list by both members of an element
I have a nested list in which I want to sort by the inner and outer elements. I have looked at other solutions on stackoverflow and tried several but none of them work the way I want them to. Below, I've presented four attempts that don't work. The comments for each block of code speak for themselves as to what I'm doing and what I want to accomplish. from operator import itemgetter # a representation of my real data to be sorted list_1 = [['key_e', ['v4eee', 'v1eee', 'v3eee', 'v2eee']], ['key_d', ['v4ddd', 'v1ddd', 'v3ddd', 'v2ddd']], ['key_a', ['v4aaa', 'v1aaa', 'v3aaa', 'v2aaa']], ['key_c', ['v4ccc', 'v1ccc', 'v3ccc', 'v2ccc']], ['key_b', ['v4bbb', 'v1bbb', 'v3bbb', 'v2bbb']]] """ # same data as above but formatted for readability list_1 = [ ['key_e', ['v4eee', 'v1eee', 'v3eee', 'v2eee']], ['key_d', ['v4ddd', 'v1ddd', 'v3ddd', 'v2ddd']], ['key_a', ['v4aaa', 'v1aaa', 'v3aaa', 'v2aaa']], ['key_c', ['v4ccc', 'v1ccc', 'v3ccc', 'v2ccc']], ['key_b', ['v4bbb', 'v1bbb', 'v3bbb', 'v2bbb']] ] """ # when running the code, pick 1 of the 4 below sort methods and comment out the other 3 # sort method #1 that doesn't work the way I want it to list_1.sort(key = lambda x: x[1]) list_1.sort(key = itemgetter(0)) """ # sort method #2 that doesn't work the way I want it to list_1 = sorted(list_1, key = lambda x: (x[0], x[1])) """ """ # sort method #3 that doesn't work the way I want it to list_1.sort(key = itemgetter(1)) list_1.sort(key = itemgetter(0)) """ """ # sort method #4 that doesn't work the way I want it to list_1.sort(key = itemgetter(0, 1)) """ # print the sorted data for d in list_1: print d[0] + ',' + d[1][0] + ',' + d[1][1] + ',' + d[1][2] + ',' + d[1][3] + '\r\n' """ # what I get using any of the sort methods key_a,v4aaa,v1aaa,v3aaa,v2aaa key_b,v4bbb,v1bbb,v3bbb,v2bbb key_c,v4ccc,v1ccc,v3ccc,v2ccc key_d,v4ddd,v1ddd,v3ddd,v2ddd key_e,v4eee,v1eee,v3eee,v2eee # what I want key_a,v1aaa,v2aaa,v3aaa,v4aaa key_b,v1bbb,v2bbb,v3bbb,v4bbb key_c,v1ccc,v2ccc,v3ccc,v4ccc key_d,v1ddd,v2ddd,v3ddd,v4ddd key_e,v1eee,v2eee,v3eee,v4eee """
I think you want the sub-lists to be sorted, and the outer list to be sorted. In which case, do it in two steps: sorted_inner = [[k, sorted(l)] for k, l in list_1] sorted_outer = sorted(sorted_inner) This gives me: sorted_outer == [['key_a', ['v1aaa', 'v2aaa', 'v3aaa', 'v4aaa']], ['key_b', ['v1bbb', 'v2bbb', 'v3bbb', 'v4bbb']], ['key_c', ['v1ccc', 'v2ccc', 'v3ccc', 'v4ccc']], ['key_d', ['v1ddd', 'v2ddd', 'v3ddd', 'v4ddd']], ['key_e', ['v1eee', 'v2eee', 'v3eee', 'v4eee']]]
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.