Loop queue algorithm issue - arrays

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.

Related

Please tell me how to fill in the fields in the list that lies on the map

I have a map
Map<Id, List<ExpenseController.MonthRow>> monthsPerKeeper = new Map<Id, List<ExpenseController.MonthRow>>();
This is how she looks:
Right now I'm only showing the months ("monthNumber") that are full.
I need to fill in all the missing ("monthNumber") and the ("amount") field for that ("monthNumber") should be 0.
It should look like this:
I tried to get the value from the map by id (that is, the List)
but I don't know how to refer to the fields in this List to fill them in
for (Id c : monthsPerKeeper.keySet()) {
if(monthsPerKeeper.containsKey(c)) {
for (ExpenseController.MonthRow a : monthsPerKeeper.get(c)) {
List<ExpenseController.MonthRow> monthsListAdd = new List<ExpenseController.MonthRow>();
for (Integer i = 1; i < 13; i++) {
if (!monthsPerKeeper.get(c).monthNumber.containsKey(i)) {
ExpenseController.MonthRow row = new ExpenseController.MonthRow();
row.monthName = ExpenseController.monthNumbers.get(i);
row.amount = 0;
row.monthNumber = i;
monthsListAdd.add(row);
}
}
}
}
}
so you have monthsListAdd with say 5 dummy months and want to add it to the original list of 7 real months? Many ways to do it, seeing how your screenshot list is unsorted and really the gaps (no expenses this month) can be anywhere... I'd probably reverse the whole thing, start with 12 placeholders with 0 amounts and just update them as I go through list...
Anyway:
After that for (ExpenseController.MonthRow a : monthsPerKeeper.get(c)) { ends add this:
monthsPerKeeper.get(c).addAll(monthsListAdd);
I stil think you'll have this in rubbish order so maybe read up about "implements Comparable" to sort your list of helper objects after they're all added up?
edit
To make it cleaner I'd use a Map where key is month number and value is... well, if all you need is Amount then probably Map<Integer,Decimal> would do. If you'd want to display more - Map<Integer, ExpenseController.MonthRow> probably. Or you know, use the fact that List could have month numbers as indexes ;)
Consider this query (works in my sandbox and returns me something but with gaps. And that's OK)
SELECT CALENDAR_MONTH(CloseDate), SUM(Amount)
FROM Opportunity
WHERE CloseDate = THIS_YEAR AND IsWon = true AND Owner.Profile.Name = 'System Administrator' AND Amount != null
GROUP BY CALENDAR_MONTH(CloseDate)
It's not even sorted - but I don't care.
Map<Integer, Decimal> myMap = new Map<Integer, Decimal>{
1 => 0,
2 => 0,
3 => 0,
4 => 0,
5 => 0,
6 => 0,
7 => 0,
8 => 0,
9 => 0,
10 => 0,
11 => 0,
12 => 0
};
for(AggregateResult ar : [SELECT CALENDAR_MONTH(CloseDate) m, SUM(Amount) a
FROM Opportunity
WHERE CloseDate = THIS_YEAR AND IsWon = true AND Owner.Profile.Name = 'System Administrator' AND Amount != null
GROUP BY CALENDAR_MONTH(CloseDate)]){
myMap.put((Integer) ar.get('m'), (Decimal) ar.get('a'));
}
for(Integer i = 1; i < 13; ++i){
System.debug(i + ' ' + DateTime.newInstance(2022,i,1).format('MMMM') + ': ' + myMap.get(i));
// instead of debug you'd have your someList.add(new ExpenseController.MonthRow(...) or whatever
}

Array of size n to array n x 2 using Java 8 stream

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 );

Groovy, insert node after current node

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]]

Merge random elements of array/split into chunks

How can I split array into chunks with some special algorithm? E.g. I need to shorten array to the size of 10 elements. If I have array of 11 elements, I want two next standing elements get merged. If I have array of 13 elements, I want three elements merged. And so on. Is there any solution?
Sample #1
var test = ['1','2','3','4','5','6','7','8','9','10','11'];
Need result = [['1'],['2'],['3'],['4'],['5|6'],['7'],['8'],['9'],['10'],['11']]
Sample #2
var test = ['1','2','3','4','5','6','7','8','9','10','11','12','13'];
Need result = [['1|2'],['3'],['4'],['5'],['6'],['7|8'],['9'],['10'],['11'],['12|13']]
Thank you in advance.
The following code most probably does what you want.
function condense(a){
var source = a.slice(),
len = a.length,
excessCount = (len - 10) % 10,
step = excessCount - 1 ? Math.floor(10/(excessCount-1)) : 0,
groupSize = Math.floor(len / 10),
template = Array(10).fill()
.map((_,i) => step ? i%step === 0 ? groupSize + 1
: i === 9 ? groupSize + 1
: groupSize
: i === 4 ? groupSize + 1
: groupSize);
return template.map(e => source.splice(0,e)
.reduce((p,c) => p + "|" + c));
}
var test1 = ['1','2','3','4','5','6','7','8','9','10','11'],
test2 = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21'];
console.log(condense(test1));
console.log(condense(test2));
A - Find the difference and create thus many random numbers for merge and put in array
B - loop through initial numbers array.
B1 - if iterator number is in the merge number array (with indexOf), you merge it with the next one and increase iterator (to skip next one as it is merged and already in results array)
B1 example:
int mergers[] = [2, 7, 10]
//in loop when i=2
if (mergers.indexOf(i)>-1) { //true
String newVal = array[i]+"|"+array[i+1]; //will merge 2 and 3 to "2|3"
i++; //adds 1, so i=3. next loop is with i=4
}
C - put new value in results array
You can try this code
jQuery(document).ready(function(){
var test = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16'];
var arrays = [];
var checkLength = test.length;
var getFirstSet = test.slice(0,10);
var getOthers = test.slice(10,checkLength);
$.each( getFirstSet, function( key,value ) {
if(key in getOthers){
values = value +'|'+ getOthers[key];
arrays.push(values);
}else{
arrays.push(value);
}
});
console.log(arrays);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

Algorithm to count occurance in a consecutive amount of time

I have a set of data that that tells me the on/off status of a component as a function of time. The data looks like this :
0 1
120 1
240 0
360 0
480 1
600 1
720 1
840 0
960 0
1080 0
1200 1
1320 1
1440 0
1560 0
1680 1
1800 0
1920 1
First column shows the time in second and the second column shows the status (1 is on, 0 is off).
I need to know the what is the 600 seconds with the most 1 in it. For example, 0-600 has 3*120 s so 360 seconds where component is on. But 120-720 has 4 etc... This means that this is not only 0-600, 600-1200, it can by any 600 seconds. The hard thing is that this is a cycle, meaning that the worst hour could be from 1920 to 480 (Note that this cycle has 2040s meaning time 2040 is also time 0). The time steps are not always equal. In this case it is a constant 120s, but it could be a mix of 120s and 150s for example.
The only thing I can think of is to scan the file from 0-600, check time is was on, 120-720, 240-840 etc... but it is very time consuming. Especially since I can have very big files.
The program is in Perl, but I only need the algorithm (if it exists). Do any of you has an idea on what would be the best approach?
Thank you
It isn't quite clear what value you're trying to calculate, but your core problem appears to be finding an algorithm for getting the samples in each sliding 600s window. The sliding window can be implemented as an array. Add new samples to the end and purge old samples to keep the total size <= 600s. The only tricky part is accounting for the time values being modulo 2040.
use strict;
use warnings;
use Data::Dump qw(pp);
my #window;
while (my $line = <DATA>) {
chomp $line;
my ($t, $on) = split / +/, $line;
# add sample to sliding window
push #window, { t => $t, on => $on};
# limit window size to 600s
while (1) {
last if #window <= 1;
my $start = $window[0]{t};
my $stop = $window[-1]{t};
# account for time values being mod 2040
if ($stop < $start) {
$stop += 2040;
}
if ($stop - $start > 600) {
# purge old sample from window
shift #window;
}
else {
last;
}
}
# do something with #window
pp \#window;
}
__DATA__
0 1
120 1
240 0
360 0
480 1
600 1
720 1
840 0
960 0
1080 0
1200 1
1320 1
1440 0
1560 0
1680 1
1800 0
1920 1
0 1
120 1
240 0
360 0
The values in #window look like this:
# no wrapping
[
{ on => 1, t => 0 },
{ on => 1, t => 120 },
{ on => 0, t => 240 },
{ on => 0, t => 360 },
{ on => 1, t => 480 },
{ on => 1, t => 600 },
]
# with wrapping
[
{ on => 0, t => 1560 },
{ on => 1, t => 1680 },
{ on => 0, t => 1800 },
{ on => 1, t => 1920 },
{ on => 1, t => 0 },
{ on => 1, t => 120 },
]
The problem may be solved in the following manner.
Define an integer n; Keep adding the ones to n until a zero occurs. That is, if a zero occurs set n to zero. In the process record the maximum n occurrence and the seconds at that time. To do this define another variable m and let m be the max value. Now if n becomes greater than m then m=n. In this way you can get near the 600s interval containing maximum number of 1s. Good luck. I hope, it worked.
Assuming that points are sorted by time, you can keep buffer of consecutive points in a way that time difference between first point and last point do not exceed 600. Here is the code in C#:
// Class for data point
class Point {
public int Time { get; private set; }
public bool On { get; private set; }
public Point(int time, bool on) {
Time = time;
On = on;
}
}
Point[] points = {
new Point(0, true),
new Point(120, true),
new Point(240, false),
new Point(360, false),
new Point(480, true),
new Point(600, true),
new Point(720, true),
new Point(840, false),
new Point(960, false),
new Point(1080, false),
new Point(1200, true),
new Point(1320, true),
new Point(1440, false),
new Point(1560, false),
new Point(1680, true),
new Point(1800, false),
new Point(1920, true),
};
int GetMaxIntervalCount(IEnumerable<Point> points) {
LinkedList<Point> buff = new LinkedList<Point>();
int buffCount = 0, res = -1;
foreach (Point point in points) {
buff.AddFirst(point);
while (buff.Count > 0) {
Point last = buff.Last.Value;
if (Math.Abs(last.Time - point.Time) > 600) {
buff.RemoveLast();
if (last.On) {
--buffCount;
}
} else {
break;
}
}
if (point.On) {
++buffCount;
}
res = Math.Max(res, buffCount);
}
return res;
}
Currently it returns only maximum count, but it can be easily adjusted to also keep actual buffer. Time complexity of a method is O(N) (each point will be
"touched" at most 2 times - on adding and deleting from buffer).

Resources