I'll try my best to explain the situation.
I have the following db columns:
oid - task - start - end - realstart - realend
My requirement is to have an output like the following:
oid1 - task1 - start1 - end1
oid2 - task2 - start2 - end2
where task1 is task, task2 is task + "real", start1 is start, start2 is realstart, end1 is end, end2 is realend
BUT
the first row should always be created (those start/end fields are never empty) the second row should only be created if realstart and realend exist which may not be true.
Inputs are 6 arrays (one for each column), Outputs must be 4 arrays, something like this:
#input oid,task,start,end,realstart,realend
#output oid,task,start,end
I was thinking about using something like oid.each but I don't know how to add nodes after the current one. Order is important in the requirement.
For any explanation please ask, thanks!
After your comment and understanding that you don't want (or cannot) change the input/output data format, here's another solution that does what you've asked using classes to group the data and make it easier to manage:
import groovy.transform.Canonical
#Canonical
class Input {
String[] oids = [ 'oid1', 'oid2' ]
String[] tasks = [ 'task1', 'task2' ]
Integer[] starts = [ 10, 30 ]
Integer[] ends = [ 20, 42 ]
Integer[] realstarts = [ 12, null ]
Integer[] realends = [ 21, null ]
List<Object[]> getEntries() {
// ensure all entries have the same size
def entries = [ oids, tasks, starts, ends, realstarts, realends ]
assert entries.collect { it.size() }.unique().size() == 1,
'The input arrays do not all have the same size'
return entries
}
int getSize() {
oids.size() // any field would do, they have the same length
}
}
#Canonical
class Output {
List oids = [ ]
List tasks = [ ]
List starts = [ ]
List ends = [ ]
void add( oid, task, start, end, realstart, realend ) {
oids << oid; tasks << task; starts << start; ends << end
if ( realstart != null && realend != null ) {
oids << oid; tasks << task + 'real'; starts << realstart; ends << realend
}
}
}
def input = new Input()
def entries = input.entries
def output = new Output()
for ( int i = 0; i < input.size; i++ ) {
def entry = entries.collect { it[ i ] }
output.add( *entry )
}
println output
Responsibility of arranging the data is on the Input class, while the responsibility of knowing how to organize the output data is in the Output class.
Running this code prints:
Output([oid1, oid1, oid2], [task1, task1real, task2], [10, 12, 30], [20, 21, 42])
You can get the arrays (Lists, actually, but call toArray() if on the List to get an array) from the output object with output.oids, output.tasks, output.starts and output.ends.
The #Canonical annotation just makes the class implement equals, hashCode, toString and so on...
If you don't understand something, ask in the comments.
IF you need an "array" whose size you don't know from the start, you should use a List instead. But in Groovy, that's very easy to use.
Here's an example:
final int OID = 0
final int TASK = 1
final int START = 2
final int END = 3
final int R_START = 4
final int R_END = 5
List<Object[]> input = [
//oid, task, start, end, realstart, realend
[ 'oid1', 'task1', 10, 20, 12, 21 ],
[ 'oid2', 'task2', 30, 42, null, null ]
]
List<List> output = [ ]
input.each { row ->
output << [ row[ OID ], row[ TASK ], row[ START ], row[ END ] ]
if ( row[ R_START ] && row[ R_END ] ) {
output << [ row[ OID ], row[ TASK ] + 'real', row[ R_START ], row[ R_END ] ]
}
}
println output
Which outputs:
[[oid1, task1, 10, 20], [oid1, task1real, 12, 21], [oid2, task2, 30, 42]]
Related
I have a database with 1 table that holds hundreds of records. I need to make a for loop in groovy script that compares first record with second record, second record with third record, etc. i need to compare length changes between records and print out all changes that is higher than 30. Example - first record 30m, second record 40m, third record 100m. It will print out second-third record.
I dont know amount of records in table, so i dont know how to create for loop. Any suggestions?
Also records has ip. Each ip can be multiple times and i need to compare all records in each ip.
record 1:
port_nbr | 1
pair | pairA
length | 30.00
add_date | 2020-06-16 00:01:13.237164
record 2:
port_nbr | 1
pair | pairA
length | 65.00
add_date | 2020-06-16 00:02:13.237164
record 3:
port_nbr | 2
pair | pairc
length | 65.00
add_date | 2020-06-16 00:02:13.237164
I expect that for loop checks if current record port_nbr is the same with next record, if yes, then it checks if pair is same and if its the same, then he compares if length changed for 30+m. In this case it would output that there is 30+m change in 1/2 record. After outputing it, then it would compare second record and third record. But they doesnt have same port_nbr and pair, so i expect it to start comparing again all port_nbr that is 2 with all following records.
There could be even 10 records with port_nbr 1, but with different pairs. I need to check for pairs aswell and only then compare lengths.
My code at this moment:
import java.sql.*;
import groovy.sql.Sql
class Main{
static void main(String[] args) {
def dst_db1 = Sql.newInstance('connection.........')
dst_db1.getConnection().setAutoCommit(false)
def sql = (" select d.* from (select d.*, lead((case when length <> 'N/A' then length else length_to_fault end)::float) over (partition by port_nbr, pair order by port_nbr, pair, d.add_date) as lengthh from diags d)d limit 10")
def lastRow = [id:-1, port_nbr:-1, pair:'', lengthh:-1.0]
dst_db1.eachRow( sql ) {row ->
if( row.port_nbr == lastRow.port_nbr && row.pair == lastRow.pair){
BigDecimal lengthChange =
new BigDecimal(row.lengthh ? row.lengthh : 0 ) - new BigDecimal(lastRow.lengthh ? lastRow.lengthh :0 )
if( lengthChange > 30.0){
print "Port ${row.port_nbr}, ${row.pair} length change: $lengthChange"
println "/tbetween row ID ${lastRow.id} and ${row.id}"
}
lastRow = row
}else{
println "Key Changed"
lastRow = row
}
}
}
}
The following code will report length changes > 30 within the same port_nbr and pair.
def sql = 'Your SQL here.' // Should include "order by pair, port_nbr, date"
def lastRow = [id:-1, port_nbr:-1, pair:'', length:-1.0]
dst_db1.eachRow( sql ) { row ->
if ( row.port_nbr == lastRow.port_nbr && row.pair == lastRow.pair ) {
BigDecimal lengthChange =
new BigDecimal( row.length ) - new BigDecimal( lastRow.length )
if ( lengthChange > 30.0 ) {
print "Port ${row.port_nbr}, ${row.pair} length change: $lengthChange"
println "\tbetween row ID ${lastRow.id} and ${row.id}"
}
lastRow = row
} else {
println "Key changed"
lastRow = row
}
}
To run the above code without a database I prefixed it with this test code:
class DstDb1 {
def eachRow ( sql, closure ) {
rows.each( closure )
}
def rows = [
[id: 1, port_nbr: 1, pair: 'pairA', length: 30.00 ],
[id: 2, port_nbr: 1, pair: 'pairA', length: 65.00 ],
[id: 3, port_nbr: 1, pair: 'pairA', length: 70.00 ],
[id: 4, port_nbr: 1, pair: 'pairA', length: 75.00 ],
[id: 5, port_nbr: 1, pair: 'pairB', length: 130.00 ],
[id: 6, port_nbr: 1, pair: 'pairB', length: 165.00 ],
[id: 7, port_nbr: 1, pair: 'pairB', length: 170.00 ],
[id: 8, port_nbr: 1, pair: 'pairB', length: 175.00 ],
[id: 9, port_nbr: 2, pair: 'pairC', length: 230.00 ],
[id:10, port_nbr: 2, pair: 'pairC', length: 265.00 ],
[id:11, port_nbr: 2, pair: 'pairC', length: 270.00 ],
[id:12, port_nbr: 2, pair: 'pairC', length: 350.00 ]
]
}
DstDb1 dst_db1 = new DstDb1()
Running the test gives this result:
Key changed
Port 1, pairA length change: 35 between row ID 1 and 2
Key changed
Port 1, pairB length change: 35 between row ID 5 and 6
Key changed
Port 2, pairC length change: 35 between row ID 9 and 10
Port 2, pairC length change: 80 between row ID 11 and 12
How would I go about compiling values from a table using a string?
i.e.
NumberDef = {
[1] = 1,
[2] = 2,
[3] = 3
}
TextDef = {
["a"] = 1,
["b"] = 2,
["c"] = 3
}
If I was for example to request "1ABC3", how would I get it to output 1 1 2 3 3?
Greatly appreciate any response.
Try this:
s="1ABC3z9"
t=s:gsub(".",function (x)
local y=tonumber(x)
if y~=nil then
y=NumberDef[y]
else
y=TextDef[x:lower()]
end
return (y or x).." "
end)
print(t)
This may be simplified if you combine the two tables into one.
You can access values in a lua array like so:
TableName["IndexNameOrNumber"]
Using your example:
NumberDef = {
[1] = 1,
[2] = 2,
[3] = 3
}
TextDef = {
["a"] = 1,
["b"] = 2,
["c"] = 3
}
print(NumberDef[2])--Will print 2
print(TextDef["c"])--will print 3
If you wish to access all values of a Lua array you can loop through all values like so (similarly to a foreach in c#):
for i,v in next, TextDef do
print(i, v)
end
--Output:
--c 3
--a 1
--b 2
So to answer your request, you would request those values like so:
print(NumberDef[1], TextDef["a"], TextDef["b"], TextDef["c"], NumberDef[3])--Will print 1 1 2 3 3
One more point, if you're interested in concatenating lua string this can be accomplished like so:
string1 = string2 .. string3
Example:
local StringValue1 = "I"
local StringValue2 = "Love"
local StringValue3 = StringValue1 .. " " .. StringValue2 .. " Memes!"
print(StringValue3) -- Will print "I Love Memes!"
UPDATE
I whipped up a quick example code you could use to handle what you're looking for. This will go through the inputted string and check each of the two tables if the value you requested exists. If it does it will add it onto a string value and print at the end the final product.
local StringInput = "1abc3" -- Your request to find
local CombineString = "" --To combine the request
NumberDef = {
[1] = 1,
[2] = 2,
[3] = 3
}
TextDef = {
["a"] = 1,
["b"] = 2,
["c"] = 3
}
for i=1, #StringInput do --Foreach character in the string inputted do this
local CurrentCharacter = StringInput:sub(i,i); --get the current character from the loop position
local Num = tonumber(CurrentCharacter)--If possible convert to number
if TextDef[CurrentCharacter] then--if it exists in text def array then combine it
CombineString = CombineString .. TextDef[CurrentCharacter]
end
if NumberDef[Num] then --if it exists in number def array then combine it
CombineString = CombineString .. NumberDef[Num]
end
end
print("Combined: ", CombineString) --print the final product.
I am trying to figure out the most elegant way of converting a simple int array (e.g. {1, 2, 3}) to a simple String array (e.g. {"id", "1", "id", "2", "id", "3"}) of String pairs using Java 8 streams.
Traditionally the code looks like this: -
int[] input = {1, 2, 3};
String[] output = new String[input.length * 2];
int i = 0;
for (int val : input) {
output[i++] = "id";
output[i++] = String.valueOf(val);
}
But assuming this can be done in a 1-liner in Java 8.
String[] result = Arrays.stream(input)
.mapToObj(x -> new String[] { "id", "" + x })
.flatMap(Arrays::stream)
.toArray(String[]::new);
Or may be a bit more verbose (but worse since we are first joining, only to split immediately after)
String[] result = Arrays.stream(input)
.mapToObj(x -> "id" + "," + x)
.collect(Collectors.joining(","))
.split(",");
I can think of these two, but it's hardly more readable of what you already have in place with a simple for loop.
Can make it even less readable than Eugene's solution:
String[] output = IntStream.range(0, input.length * 2)
.mapToObj(x -> x % 2 == 0 ? "id" : input[x / 2 ] + "")
.toArray(String[]::new);
And another variation of this can be next:
String[] result = Arrays.stream( input )
.boxed()
.flatMap( x -> Stream.of( "id", Integer.toString( x ) ) )
.toArray( String[]::new );
i need help, about how to replace my array2d with another array1d
example array2d, that i have
role = {{"mike", "30", "1"},
{"mike", "50", "3"}}
i want to replace the third array value "role[...][3]" with this array1d
role_listname = {
[1] = "Winner!",
[2] = "Funnier!",
[3] = "Crazy!"
}
so the result i got.
1. Winner - 30p
2. Crazy - 50p
Not like
1. Winner - 30p
2. Funnier - 40p
my code :
for i = 1, #role do
role[i][3] = role_listname[i]
print(i .. ". " .. role[i][3] .. " - " .. role[i][2])
end
i don't know. it's not working, could you tell me how it's work ?
You logic is wrong. You are using the loop variable i as index, but you want to use the corresponding entry in the role table:
role = {
{"mike", "30", 1},
{"mike", "50", 3}
}
role_listname = {
[1] = "Winner!",
[2] = "Funnier!",
[3] = "Crazy!"
}
for i = 1, #role do
role[i][3] = role_listname[role[i][3]] -- here is the change
print(i .. ". " .. role[i][3] .. " - " .. role[i][2])
end
Note that i also switched the indices in the role table to numerics. But this does not really matter, you could use any keys. They just have to match with the corresponding keys in the role_listname table.
I'm trying to do the following, and can't figure out how exactly do It without crashing or infinite looping:
I have to create a queue in which I have to distribute different tasks a different number of times each, alternatively with this kind of info:
Task X: [NextOne,LastOne]
Task 1: [30,32]
Task 2: [76,81]
Task 3: [2,2]
Task 4: [5,8]
Meaning that "Task X" will be made "LastOne - NextOne" times, and if both are equal, it won't be enqueued, and they enter the queue in X order.
With this example, the queue should look like:
FIRST
Task1[30]
Task2[76]
Task4[5]
Task1[31]
Task2[77]
Task4[6]
Task1[32]
Task2[78]
Task4[7]
Task2[79]
Task4[8]
Task2[80]
Task2[81]
LAST
It's not a language issue, it's more of an algorithm issue I have here. Using PHP I've made the following:
$tasks = array(
'Task1' => array(30,32),
'Task2' => array(76,81),
'Task3' => array(2,2),
'Task4' => array(5,8)
);
$aux = array();
$i=0;
foreach($tasks as $s=>$n) {
$aux[$i]['task'] = $s;
$aux[$i]['times'] = $n[1]-$n[0];
$aux[$i]['first'] = $n[0];
$i++;
}
But as you imagine this actually does nothing, just change the shape of the information. I'm really stuck here I don't know why, this actually shouldn't be hard to figure out. I'd appreciate any help.
In python (I may be misinterpreting your "it's not a language issue" comment - forgive me):
tasks = [
("Task1", 30, 32),
("Task2", 76, 81),
("Task3", 2, 2),
("Task4", 5, 8) ]
while not tasks == []:
# Pop first task off the current list
(n, s, e) = tasks[0]
tasks = tasks[1:]
print n, s
if s != e:
tasks.append( (n, s+1, e) )
Sorry it's not in php - it's not my forte, but perhaps this'll help? Output:
Task1 30
Task2 76
Task3 2
Task4 5
Task1 31
Task2 77
Task4 6
Task1 32
Task2 78
Task4 7
Task2 79
Task4 8
Task2 80
Task2 81
I guess you can use $s as a key for the hash(or array equivalent) and just increase the value by 1 whenever it encounters the element with the same key. The default value would be 0 in this case.
For example,
Task1 => array(30,32)
will be come in order like
Task1[30] (default value to 0)
...
...
Task1[31] (add 1 which becomes 1)
...
Task1[32] (add 1 which becomes 2)
This means that Task1 has appeared 3 times overall, and the final times value should be 2.
I think you can use array_key_exists helper function to check if certain task had appeared previously.
In C#
The result is as you wish.
I added a number as a flag: 1 = Not to be enqueued, 2 = Last record of the task.
Not efficient but works!
private static void Main()
{
var tasks = new Dictionary<string, int[]>
{
{"Task1", new[] {30, 32, 0}},
{"Task2", new[] {76, 81, 0}},
{"Task3", new[] {2, 2, 0}},
{"Task4", new[] {5, 8, 0}}
};
int loopCounter = 0;
Console.WriteLine("FIRST");
while (loopCounter < tasks.Count)
{
foreach (var task in tasks)
{
if (task.Value[0] == task.Value[1])
{
if (task.Value[2] == 2)
{
loopCounter++;
Console.WriteLine(task.Key + "[" + task.Value[0] + "]");
task.Value[2] = 1;
}
else if (task.Value[2] == 0)
{
loopCounter++;
task.Value[2] = 1;
}
}
else
{
Console.WriteLine(task.Key + "[" + task.Value[0] + "]");
task.Value[0]++;
if (task.Value[0] == task.Value[1])
task.Value[2] = 2;
}
}
}
Console.WriteLine("LAST");
Console.ReadLine();
}
Output:
FIRST
Task1[30]
Task2[76]
Task4[5]
Task1[31]
Task2[77]
Task4[6]
Task1[32]
Task2[78]
Task4[7]
Task2[79]
Task4[8]
Task2[80]
Task2[81]
LAST
Hope this helps.