Create a new array from existing hash based on a keys value - ruby

[{"date"=>"2020-09-15", "work_category"=>"CASE PICKS", "count"=>"21315"},
{"date"=>"2020-09-15", "work_category"=>"LOADING", "count"=>"6401"},
{"date"=>"2020-09-15", "work_category"=>"PALLET STAGING", "count"=>"6649"},
{"date"=>"2020-09-15", "work_category"=>"PUTAWAY", "count"=>"4974"},
{"date"=>"2020-09-15", "work_category"=>"RECEIVING", "count"=>"4108"},
{"date"=>"2020-09-16", "work_category"=>"CASE PICKS", "count"=>"16842"},
{"date"=>"2020-09-16", "work_category"=>"LOADING", "count"=>"4987"},
{"date"=>"2020-09-16", "work_category"=>"PALLET STAGING", "count"=>"5716"},
{"date"=>"2020-09-16", "work_category"=>"PUTAWAY", "count"=>"6292"},
{"date"=>"2020-09-16", "work_category"=>"RECEIVING", "count"=>"4894"},
{"date"=>"2020-09-17", "work_category"=>"CASE PICKS", "count"=>"24849"},
{"date"=>"2020-09-17", "work_category"=>"LOADING", "count"=>"5303"},
{"date"=>"2020-09-17", "work_category"=>"PALLET STAGING", "count"=>"5753"},
{"date"=>"2020-09-17", "work_category"=>"PUTAWAY", "count"=>"5136"},
{"date"=>"2020-09-17", "work_category"=>"RECEIVING", "count"=>"4699"},
{"date"=>"2020-09-18", "work_category"=>"CASE PICKS", "count"=>"11806"},
{"date"=>"2020-09-18", "work_category"=>"LOADING", "count"=>"5147"},
{"date"=>"2020-09-18", "work_category"=>"PALLET STAGING", "count"=>"5426"},
{"date"=>"2020-09-18", "work_category"=>"PUTAWAY", "count"=>"5110"},
{"date"=>"2020-09-18", "work_category"=>"RECEIVING", "count"=>"4534"},
{"date"=>"2020-09-19", "work_category"=>"CASE PICKS", "count"=>"8086"},
{"date"=>"2020-09-19", "work_category"=>"LOADING", "count"=>"1753"},
{"date"=>"2020-09-19", "work_category"=>"PALLET STAGING", "count"=>"2201"},
{"date"=>"2020-09-19", "work_category"=>"PUTAWAY", "count"=>"1341"},
{"date"=>"2020-09-19", "work_category"=>"RECEIVING", "count"=>"1052"},
{"date"=>"2020-09-20", "work_category"=>"CASE PICKS", "count"=>"5160"},
{"date"=>"2020-09-20", "work_category"=>"LOADING", "count"=>"2690"},
{"date"=>"2020-09-20", "work_category"=>"PALLET STAGING", "count"=>"2663"},
{"date"=>"2020-09-20", "work_category"=>"PUTAWAY", "count"=>"1554"},
{"date"=>"2020-09-20", "work_category"=>"RECEIVING", "count"=>"1536"},
{"date"=>"2020-09-21", "work_category"=>"CASE PICKS", "count"=>"2202"},
{"date"=>"2020-09-21", "work_category"=>"LOADING", "count"=>"860"},
{"date"=>"2020-09-21", "work_category"=>"PALLET STAGING", "count"=>"1004"},
{"date"=>"2020-09-21", "work_category"=>"PUTAWAY", "count"=>"1238"},
{"date"=>"2020-09-21", "work_category"=>"RECEIVING", "count"=>"954"}]
I need to pull out this dataset/hash/array from the above hash:
Logic:
Grab an array that lists the count keys values for each work_category row that matches the provided work_category key value of “CASE PICKS” for every date listed (maintaining the date order oldest -> newest).
Desired Result Set:
[ “21315”, “16842”, “24849”, “11806”, “8086”, “5160”, “2202” ]
Seems basic but I'm having some troubles... very green to Ruby. Anyone that could provide some help -- it would be greatly appreciated!

The following is a sub-array of the array given in the question.
arr = [
{"date"=>"2020-09-15", "work_category"=>"CASE PICKS", "count"=>"21315"},
{"date"=>"2020-09-15", "work_category"=>"LOADING", "count"=>"6401"},
{"date"=>"2020-09-15", "work_category"=>"PALLET STAGING", "count"=>"6649"},
{"date"=>"2020-09-15", "work_category"=>"PUTAWAY", "count"=>"4974"},
{"date"=>"2020-09-15", "work_category"=>"RECEIVING", "count"=>"4108"},
{"date"=>"2020-09-16", "work_category"=>"CASE PICKS", "count"=>"16842"},
{"date"=>"2020-09-16", "work_category"=>"LOADING", "count"=>"4987"},
{"date"=>"2020-09-16", "work_category"=>"PALLET STAGING", "count"=>"5716"},
{"date"=>"2020-09-16", "work_category"=>"PUTAWAY", "count"=>"6292"},
{"date"=>"2020-09-16", "work_category"=>"RECEIVING", "count"=>"4894"},
{"date"=>"2020-09-17", "work_category"=>"CASE PICKS", "count"=>"24849"},
{"date"=>"2020-09-17", "work_category"=>"LOADING", "count"=>"5303"}
]
It is only necessary to make a single pass through the array.
arr.each_with_object([]) do |h,arr|
arr << h["count"] if h["work_category"] == "CASE PICKS"
end
#=> ["21315", "16842", "24849"]

First, filter the items with select and then extract just the counts with map.
counts = arr
.select { |item| item["work_category"] == "CASE PICKS" }
.map { |item| item["count"] }

Related

How can I get next Git commit by date?

In a script (msbuild) on a windows machine, I have the SHA1 of a commit.
What is the correct git command, to get the "next" (by date) commit SHA1 in the same branch?
Take a look at this example log from TortoiseGit:
In my script I currently have the 4b60a7e87762f421ddeee4ea0282a99c5db20e4a. Now I need a command to get c0fb4c86c354cfe32c6d0f1753958ab60db7e086.
I ended up writing a Custom MSBuild-Task which finds the predecessor. I post it in case it might come in handy for someone else.
Based on the comments of joanis I ended up looking in the git log for the next commit. So in my msbuild-script I now have a target which finds the next git commit and executes a specific target TargetToExecuteForNextGitCommit and sets the property CurrentCommitHash to the hash value of the next git commit. So this looks like:
<Target Name="DoTargetForNextGitCommit">
<!-- 6. Nachfolger vom aktuellen Commit finden -->
<!-- 6.a Tag des aktuellen Commits finden. Damit die Anzahl der zu durchsuchenden Commits einschränken.
Denn ich suche ja nur nach Commits, die danach kamen. -->
<Exec Command="git log --format=%25%25cd --date=iso-strict -n 1 $(CurrentCommitHash)"
WorkingDirectory="$(GitRepoDir)"
ConsoleToMSBuild="true">
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommitTimestamp"/>
</Exec>
<Message Text="Commit-Zeitstempel: $(GitCommitTimestamp)"/>
<!-- 6.b Git-Log holen, in eine ItemGroup packen, den Index des Elements finden, das meinen Hash hat
und dann den Nachfolger holen und dessen hash finden -->
<!-- 6.b.i Git-Log holen -->
<Exec Command="git log --date-order --format=%25%25H --after=$(GitCommitTimestamp) origin/master"
WorkingDirectory="$(GitRepoDir)"
ConsoleToMSBuild="true">
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommitHashesAfterCommitDay"/>
</Exec>
<ItemGroup>
<GitCommitHashesAfterCommitDayEntries Include="$(GitCommitHashesAfterCommitDay)"/>
</ItemGroup>
<!-- 6.b.ii Nachfolger holen-->
<GetNextCommit Items="#(GitCommitHashesAfterCommitDayEntries)" CurrentCommitHash="$(CurrentCommitHash)">
<Output TaskParameter="NextCommitHash" PropertyName="NextCommitHashProperty"/>
</GetNextCommit>
<Message Text="Nächster Commit: $(NextCommitHashProperty)" />
<!-- 7. Wenn es einen Nachfolger gibt, dann beginne mit diesem wieder bei 1. -->
<MSBuild Projects="$(MSBuildThisFile)"
Targets="$(TargetToExecuteForNextGitCommit)"
Properties="CurrentCommitHash=$(NextCommitHashProperty)"
Condition=" '$(NextCommitHashProperty)' != '0' "/>
</Target>
<UsingTask TaskName="GetNextCommit" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
<ParameterGroup>
<Items ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<CurrentCommitHash ParameterType="System.String" Required="true" />
<NextCommitHash ParameterType="System.String" Output="true" Required="false" />
</ParameterGroup>
<Task>
<Using Namespace="System.Linq"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
var hashes = Items.Select(i => i.ItemSpec).ToList();
var currentCommitIndex = hashes.IndexOf(CurrentCommitHash);
if (currentCommitIndex < 1)
{
NextCommitHash = "0";
}
else
{
var nextCommitIndex = currentCommitIndex - 1;
NextCommitHash = hashes[nextCommitIndex];
}]]>
</Code>
</Task>
</UsingTask>

How to Convert XPath Nodelist Attributes to Strings

I have an XPath expression that returns the following items but need to get the values from them. (respectively "2" & "3" but entire attribute would work too)
(my xml and path expression is here: Getting Upper XPath Value Based on Lower XML value)
Attribute='SERepeatKey =2'
Attribute='SERepeatKey =3'
This doesn't work
NodeList nl = (NodeList) xPath.compile(strXPath).evaluate(docODM, XPathConstants.NODESET);
for (int x = 0; x < nl.getLength(); x++){
Node ser = nl.item(x);
String sSer = ser.getAttributes().getNamedItem("SERepeatKey").getNodeValue().toString();
}
I removed the #SERepeatKey from the previous XPath statement to return the entire xml nodes then the above statement worked returning the "2" and "3" values.
<Data SOID="(DEV)" MetaDataVersionOID="" >
<SData SKey="003-003" >
<SiteRef LOID="SE 003"/>
<SEData SEOID="UNSCHEDULED" SERepeatKey="2">
<FData FOID="REQUIRED" FormRepeatKey="1">
<IGData IGOID="REQUIRED" IGRepeatKey="0" TransactionType="Upsert">
<IData ItemOID="REQUIRED.REQ_V" Value="1" />
</IGData>
</FData>
</SEData>
</SData>
</Data>
<Data SOID="(DEV)" MetaDataVersionOID="" >
<SData SKey="003-003" >
<SiteRef LOID="SE 003"/>
<SEData SEOID="UNSCHEDULED" SERepeatKey="3">
<FData FOID="REQUIRED" FormRepeatKey="1">
<IGData IGOID="REQUIRED" IGRepeatKey="0" TransactionType="Upsert">
<IData ItemOID="REQUIRED.REQ_V" Value="1" />
</IGData>
</FData>
</SEData>
</SData>

How to move sections of XML with Ruby and Nokogiri between XML files

I have a program that stores configuration data in a single XML file. The program is installed on two servers, production and dev. I want to move a piece of the XML file into a git repository and push it from dev to production.
The XML file has a section for each project so I only need the section for the project I’m working on. Here is an example:
<?xml version="1.0" encoding="UTF-8"?>
<Timers>
<timer AUTOSTART="0" CONTAINER="" CONTAINER_PWD="" CONTAINER_USER="" LOG_ACTIVE="INFO" LOG_SIZE="25" MAXJOBS="10" MAXLOGSIZE="5000" NAME="OrderImports" OWNPROCESS="0" VMARGS="">
<job DATE_REPEAT="0" DAY="0" DESCRIPTION="Send CertainTeed ShipFile - Once a day" DISABLE="0" FRI="1" HIGH_PRIORITY="0" HOUR="23" INTHOUR="" INTMIN="" INTSEC="" JOBID="66e6aa67-0ad0-4480-91ae-d17c3e769f4a" MIN="00" MON="1" MONTH="0" RUNONCE="0" SAT="0" SUN="0" THU="1" TIMEOUTHOUR="" TIMEOUTMIN="04" TIMEOUTSEC="" TOHOUR="" TOMIN="" TUE="1" WED="1" YEAR="0">
<unit>order_imports.PutCertainTeedShipFile</unit>
</job>
<job DATE_REPEAT="0" DAY="0" DESCRIPTION="Send CertainTeed BudgetFile - Once a day" DISABLE="0" FRI="1" HIGH_PRIORITY="0" HOUR="22" INTHOUR="" INTMIN="" INTSEC="" JOBID="dca2f65a-08d8-4dd3-b663-ccaf4718c558" MIN="00" MON="1" MONTH="0" RUNONCE="0" SAT="0" SUN="0" THU="1" TIMEOUTHOUR="" TIMEOUTMIN="04" TIMEOUTSEC="" TOHOUR="" TOMIN="" TUE="1" WED="1" YEAR="0">
<unit>order_imports.PutCertainTeedBudgetFile</unit>
</job>
<job DATE_REPEAT="0" DAY="0" DESCRIPTION="Send CertainTeed BudgetInvoice File - Once a day" DISABLE="0" FRI="1" HIGH_PRIORITY="0" HOUR="23" INTHOUR="" INTMIN="" INTSEC="" JOBID="12ccb8dd-1f06-4767-a86e-364fd5a4ff52" MIN="30" MON="1" MONTH="0" RUNONCE="0" SAT="0" SUN="0" THU="1" TIMEOUTHOUR="" TIMEOUTMIN="" TIMEOUTSEC="" TOHOUR="" TOMIN="" TUE="1" WED="1" YEAR="0">
<unit>order_imports.PutCertainTeedBudgetInvoice</unit>
</job>
<job DATE_REPEAT="0" DAY="0" DESCRIPTION="Send CertainTeed Shiproof File - Once a day" DISABLE="0" FRI="1" HIGH_PRIORITY="0" HOUR="23" INTHOUR="" INTMIN="" INTSEC="" JOBID="6ba478a6-82aa-45c9-abb3-cde1429012fe" MIN="30" MON="1" MONTH="0" RUNONCE="0" SAT="0" SUN="0" THU="1" TIMEOUTHOUR="" TIMEOUTMIN="" TIMEOUTSEC="" TOHOUR="" TOMIN="" TUE="1" WED="1" YEAR="0">
<unit>order_imports.PutCertainTeedShipFileRoof</unit>
</job>
</timer>
<timer AUTOSTART="0" CONTAINER="" CONTAINER_PWD="" CONTAINER_USER="" LOG_ACTIVE="INFO" LOG_SIZE="25" MAXJOBS="10" MAXLOGSIZE="5000" NAME="CPC8" OWNPROCESS="0" VMARGS="">
<job DATE_REPEAT="0" DAY="0" DESCRIPTION="CPC8" DISABLE="0" FRI="1" HIGH_PRIORITY="0" HOUR="05" INTHOUR="" INTMIN="02" INTSEC="" JOBID="804dfb35-d5a6-4075-b55c-328cf556da7c" MIN="00" MON="1" MONTH="0" RUNONCE="0" SAT="1" SUN="1" THU="1" TIMEOUTHOUR="" TIMEOUTMIN="" TIMEOUTSEC="" TOHOUR="01" TOMIN="30" TUE="1" WED="1" YEAR="0">
<unit>cpc8.cpc8AvettiMain</unit>
</job>
<job DATE_REPEAT="0" DAY="0" DESCRIPTION="CPC82" DISABLE="0" FRI="1" HIGH_PRIORITY="0" HOUR="05" INTHOUR="" INTMIN="02" INTSEC="" JOBID="66cbe9a2-d79a-4cfd-9f21-b1c72f04aaa5" MIN="00" MON="1" MONTH="0" RUNONCE="0" SAT="1" SUN="1" THU="1" TIMEOUTHOUR="" TIMEOUTMIN="" TIMEOUTSEC="" TOHOUR="01" TOMIN="30" TUE="1" WED="1" YEAR="0">
<unit>cpc8.cpc8AvettiMain2</unit>
</job>
<job DATE_REPEAT="0" DAY="0" DESCRIPTION="CPC8 Inbound Order Shop" DISABLE="0" FRI="1" HIGH_PRIORITY="0" HOUR="05" INTHOUR="" INTMIN="05" INTSEC="" JOBID="05fb0f4c-f175-4e10-a885-63d2aa07f2ef" MIN="00" MON="1" MONTH="0" RUNONCE="0" SAT="1" SUN="1" THU="1" TIMEOUTHOUR="" TIMEOUTMIN="" TIMEOUTSEC="" TOHOUR="01" TOMIN="30" TUE="1" WED="1" YEAR="0">
<unit>cpc8.cpc8InboundLauncherDev2</unit>
</job>
<job DATE_REPEAT="0" DAY="0" DESCRIPTION="CPC8 Sync Journal Monitor" DISABLE="0" FRI="1" HIGH_PRIORITY="0" HOUR="07" INTHOUR="03" INTMIN="" INTSEC="" JOBID="8bb67c1a-6935-4069-8c04-48d53b678d75" MIN="00" MON="1" MONTH="0" RUNONCE="0" SAT="1" SUN="1" THU="1" TIMEOUTHOUR="" TIMEOUTMIN="" TIMEOUTSEC="" TOHOUR="22" TOMIN="" TUE="1" WED="1" YEAR="0">
<unit>cpc8.syncMonitor</unit>
</job>
</timer>
This example contains two projects "OrderImports" and "CPC8". I need the "OrderImports" project, identified by the name attribute on the timer node.
I used a Ruby script with Nokogiri to parse the relevant information from the XML file. I’m using to_xml output and saving it to a file in the git repo.
Here is the code that will save it to a text file. How do I restore the node from this text file back to the original file?
def find_timers_by_name
timers = []
nodes = #timer_noko.xpath("Timers/timer/job/unit[contains(text(), '#{#project_name}')]")
nodes.each do |node|
timers << node.parent.parent.attribute("NAME")
end
return timers.uniq
end
def dump_timer_xml
timers = find_timers_by_name
timer_dir = "./int_config/timers"
FileUtils.mkdir_p(timer_dir)
timers.each do |timer|
nodes = #timer_noko.xpath("Timers/timer[#NAME='#{timer}']")
nodes.each do |node|
timer_xml_file = File.join(timer_dir, "#{timer}.xml")
tx = "\t#{node.to_xml}"
File.open(timer_xml_file, 'w') { |file| file.write(tx) }
end
end
end
I would like to take it a step further and not replace the node unless something has actually changed or the node does not exist.
I need to have the script check to see if the project section exists in the main XML file and, if so, replace it with the one from the git repo. I’m hoping that Nokogiri has some sort of built-in methods for doing this. I do not need to use to_xml if that’s not going to work.

How do I parse this xml using linq?

So, I need just the value of those equals that are immediately within the <and> and not within the <not>. So, I need the the following: 7942 and 6252. How do I get these?
<aaa>
<and>
<equal ident="qwe">7942</equal>
<equal ident="qwe">6252</equal>
<not>
<equal ident="qwe">1056</equal>
</not>
<not>
<equal ident="qwe">6562</equal>
</not>
</and>
</aaa>
Either this is a invalid xml, and you cannot parse it with Linq, or you have typo, and opening tag names should match closing tag names, e.g:
<aaa>
<and>
<equal ident="qwe">7942</equal>
<equal ident="qwe">6252</equal>
<not>
<equal ident="qwe">1056</equal>
</not>
<not>
<equal ident="qwe">6562</equal>
</not>
</and>
</aaa>
Parsing this xml looks like:
var xdoc = XDocument.Load(path_to_xml);
var result = xdoc.Descendants("and").Elements("equal").Select(e => (int)e);
Gives:
7942
6252
Same with XPath
var result = xdoc.XPathSelectElements("//and/equal").Select(e => (int)e);

xpath selection of a title node within a resultset

I've an xml doc like below. I was trying to select a title node with a particular value in it say "![CDATA[ 1234 ]]". That Title node may be in any Type node. I was using this xpath query
/Results/ResultSet/Type[Title="![CDATA[ 1234 ]]"]
but didnt get anything selected. can someone pls help.
<Results>
<Info>...</Info>
<ResultSet num="4">
<Type type="A">
<Title>
<![CDATA[ 1234 ]]>
</Title>
<Description>
<![CDATA[ 1234 ]]>
</Description>
<Domain>
<![CDATA[1234 ]]>
</Domain>
<Target>
<![CDATA[]]>
</Target>
</Type>
<Type type="A">
<Title>
<![CDATA[ abcdef ]]>
</Title>
<Description>
<![CDATA[abcdef]]>
</Description>
<Domain>
<![CDATA[abcdef]]>
</Domain>
<Target>
<![CDATA[abcdef]]>
</Target>
</Type>
EDIT: included the ruby code that I am using
doc = Nokogiri::HTML(html)
Element = doc.xpath('/Results/ResultSet/Type/Title[text()=" 1234 "]')
if Element.empty?()
puts "not there "
else
Element.each do |node|
puts "Found Title: #{node.text}"
end
end
end
The XPath is wrong:
Use this:
/Results/ResultSet/Type/Title[text()=" 1234 "]
Based on the link OP posted for the XML, here is the working XPath:
/QuigoResults/ResultSet/Listing/Title[text()=" location in DYNAMICREGION "]

Resources