Calendar.set error or infinite loop - groovy

the following does not seem to work, it seems to cause an infinite loop:
import java.text.SimpleDateFormat;
SimpleDateFormat out=new SimpleDateFormat('yyyy-MM-dd');
def from = Calendar.instance
from.set(year: 2017, month: Calendar.JANUARY, date: 3)
def to = Calendar.instance
to.set(year: 2017, month: Calendar.FEBRUARY, date: 3)
from.upto(to) {
cal=it;
prev=cal;
prev.set(Calendar.DAY_OF_MONTH, 1);
println out.format(prev.getTime());
}
can somebody please explain why this should not work? I don't get it. My goal is to get the first day of month within the upto loop.

Inside the loop, you are constantly setting the calendar back to the first day of the month...
It's similar to if you did:
for (int i = 0; i < 10; i++) {
i = 0
println i
}
(that will never finish either)
Also, you code will run for every day between the two dates... which I don't think is what you are looking for either
It's easier if you use immutable things over Calendar, and as you're on Java 8, you can do:
import java.time.*
import java.time.format.*
// Add a next method, so you can do ranges of LocalDates
LocalDate.metaClass.next = { delegate.plusDays(1) }
LocalDate from = LocalDate.of(2017, 1, 3)
LocalDate to = LocalDate.of(2017, 2, 3)
(from..to).each {
println it.format(DateTimeFormatter.ISO_DATE) + " : " + it.withDayOfMonth(1).format(DateTimeFormatter.ISO_DATE)
}

Related

Loop with no duplicate values

Description of the problem.
Choose a number between 0 and 4 (the randomly number will indicate how many values from the list will be displayed)
Get random values from list, so that they are unique and display as a result.
My code does not work, please let me know how to fix it. I will be grateful for your help.
import groovy.json.JsonOutput
import java.util.Random
Random random = new Random()
def num = ["0","1","2","3","4"]
def randomNum = random.nextInt(num.size())
def min = 0;
def max = num[randomNum];
def list = ["Toy", "Mouse", "Cup","Book","Tiger"]
while(max > min) {
def randomValue = random.nextInt(list.size())
def theValue = list[randomValue] + '"'+ "," +
max++;
}
The result that I would like to achieve is for example:
Toy","Cup (if 2 is randomly selected)
Toy","Tiger","Book" (if 3 is randomly selected)
the available number is from 0 to 4 as many as there are possible
elements to choose from 0 - Toy, 1 - Mouse 2- Cup 3- Book 4 - tiger.
First, a number, e.g. 2, is drawn and then 2 elements are selected
randomly from the list of values.
You could do something like this:
Random random = new Random()
def list = ["Toy", "Mouse", "Cup","Book","Tiger"]
// this allows zero to be selected... if that is a violation
// of the requirement, adjust this....
int numberOfElementsToSelect = random.nextInt(list.size())
def results = []
numberOfElementsToSelect.times {
results << list.remove(random.nextInt(list.size()))
}
println results
println results.join(',')
EDIT:
Works great, I have one more question what to do to exit the script
without showing any results in case the value is empty
If you want to exit the script without showing results, you could do something like this:
Random random = new Random()
def list = ["Toy", "Mouse", "Cup","Book","Tiger"]
// this allows zero to be selected... if that is a violation
// of the requirement, adjust this....
int numberOfElementsToSelect = random.nextInt(list.size())
def results = []
numberOfElementsToSelect.times {
results << list.remove(random.nextInt(list.size()))
}
if(results) {
// do what you want with the results, like...
println results.join(',')
} else {
// do something else, could be exit the script...
System.exit(-2)
}

Selecting a date between certain range based on conditions using groovy

I am having a little issue with a date generator code below. The list of the code below is to ensure that a random date is selected within the summer months (May, June, July August) for availability.
So what I did is that I say if the current month is less than 5 (less than May), then select a random date between 1st May this year till 31st August this year, else if the current month is past 7 (past July), then select a random date between 1st May next year till 31st August next year.
Now I notice a little glitch in my code I require help with. As I ran the code below today (8th May), it is possible that the date the random generator selects could be in May before today's date. Actually the issue is I don't have anything to handle when I am in the current months. So I think I require a little refactoring.
What i would like is that it checks the current date and if it between May to July (so not less than May or more than July), then check today's date and pick a date between today till the 31st August this year.
My brain has been fried and for some strange reason I am struggling on something which logically makes sense, but I've just been having issues programming it.
import groovy.time.TimeCategory
//def dataSet = testRunner.testCase.getTestStepByName("Properties")
// Select the current test data line
def dateFormat = 'yyyy-MM-dd'
def getNumberInRange = { min, max -> new Random().nextInt(max + 1 - min) + min }
def isTodayBeforeMay = { Calendar.MONTH < 5 }
def isTodayAfterJuly = { Calendar.MONTH > 7 }
//Get the number of days between today and given date
def getDifferenceDays = { targetDate, closure ->
def strDate = closure (targetDate)
def futureDate = new Date().parse(dateFormat, strDate)
TimeCategory.minus(futureDate, new Date()).days
}
//Get the offset between today and max date i.e.,31 august
def getOffSetDays = { date ->
//Need to change the date range if needed.
//As per OP, May to August is mentioned below
def max = getDifferenceDays(date) { "${it[Calendar.YEAR]}-08-31" }
def min = getDifferenceDays(date) { "${it[Calendar.YEAR]}-05-01" }
getNumberInRange(min, max)
}
def now = new Date()
def nextYearNow = now.updated(year: now[Calendar.YEAR] + 1)
def selected
def finalDate
log.info "Today : $now"
log.info "Next year same date : $nextYearNow"
if (isTodayBeforeMay()) {
selected = now
} else if (isTodayAfterJuly()) {
selected = nextYearNow
} else {
throw new Error("Not implemented for the days between 1st May to 30th July")
}
def offset = getOffSetDays(selected)
//Add the offset days to now
use(TimeCategory) {
finalDate = now + offset.days
}
All you need is to implement the else condition instead of throw new Error(..) below (code excerpt from the question):
If you read the code, it is crystal clear each condition and error message as place holder for the unknown data range in below and which is now you wanted it to be handled.
if (isTodayBeforeMay()) {
selected = now
} else if (isTodayAfterJuly()) {
selected = nextYearNow
} else {
throw new Error("Not implemented for the days between 1st May to 30th July")
}
Just add the below statement in the last else in place of threw new Error
selected = getOffSetDays(now)
EDIT:
You can try quickly online Demo
EDIT 2: Looks the above is not working at times randomly, so updating the answer:
import groovy.time.TimeCategory
def dateFormat = 'yyyy-MM-dd'
def getNumberInRange = { max, min = 1 -> new Random().nextInt(max + 1 - min) + min }
def isTodayBeforeMay = { Calendar.MONTH < 5 }
def isTodayAfterJune = { Calendar.MONTH > 6 }
//Get the number of days between today and given date
def getDifferenceDays = { targetDate, closure ->
def strDate = closure (targetDate)
def futureDate = new Date().parse(dateFormat, strDate)
TimeCategory.minus(futureDate, new Date()).days
}
def getPaddedString = { num, len = 2, padwith = '0' ->
num.toString().padLeft(len, padwith)
}
//Get the offset between today and max date i.e.,31 august
def getOffSetDays = { date, minMonth = 5, minDay = 1 ->
//Need to change the date range if needed.
//As per OP, May to August is mentioned below
def max = getDifferenceDays(date) { "${it[Calendar.YEAR]}-08-31" }
def min = getDifferenceDays(date) { "${it[Calendar.YEAR]}-${getPaddedString(minMonth)}-${getPaddedString(minDay)}" }
getNumberInRange(max, min)
}
def now = new Date()
def nextYearNow = now.updated(year: now[Calendar.YEAR] + 1)
def selected
def finalDate
println "Today : $now"
println "Next year same date : $nextYearNow"
if (isTodayBeforeMay()) {
selected = now
} else if (isTodayAfterJune()) {
selected = nextYearNow
}
def dayz = getNumberInRange(getDifferenceDays(now) { "${it[Calendar.YEAR]}-08-31" })
def offset = selected ? getOffSetDays(selected) : dayz
offset = offset > 0 ? offset : now[Calendar.DAY_OF_MONTH]+1
//Add the offset days to now
use(TimeCategory) {
finalDate = now + offset.days
}
println "Final future date is : $finalDate"
println "Final future date is(formatted) : ${finalDate.format(dateFormat)}"
assert now <= finalDate
This Demo generate the date 1000 times just to make sure the date is ok.

Function which cut string and return integer

I need function whitch from data (always yyyy-mm-dd for example 2010-01-01) cut only month and return me integer not string. But if this month is for example January and first number is 0 function should cut this 0 and return me only 1. I try to do this wuth substr but i always have null. For example from 2014-02-03 fnction return 2. Anyone can help me?
I try to use
date('m', strtotime('$myDate'))
Its work with years but it useless for me becosue it return me first 01, 02 or example but i need need this withot this 0 becouse i transfer this to array where i have 12 numbers without 0.
See this exemple with JAVA :
public Integer getMonth(String date) {
date = date.substring(5,7);
if(date.charAt(1) == 0) {
date.substring(1);
}
return Integer.parseInt(date);
}
The function return 2 for date = "2016-02-10"
This is the same for PHP.
This exemple with PHP :
<?php
public function getMonth($date) {
$date = substr($date, 5, 2);
if(date[0] == 0) {
$date = substr($date,1)
}
return $date;
}
?>
I can't try this because I don't have apache in my computer because I work with JAva and ASP.Net.

Is There a Groovier Way To Add Dashes to a String?

I have the following code, which works, but I'm wondering if there is a "groovier" way of doing this:
/**
* 10 digit - #-######-##-#
* 13 digit - ###-#-######-##-#
* */
private formatISBN(String isbn) {
if (isbn?.length() == 10) {
def part1 = isbn.substring(0, 1)
def part2 = isbn.substring(1, 7)
def part3 = isbn.substring(7, 9)
def part4 = isbn.substring(9, 10)
return "${part1}-${part2}-${part3}-${part4}"
} else if (isbn?.length() == 13) {
def part1 = isbn.substring(0, 3)
def part2 = isbn.substring(3, 4)
def part3 = isbn.substring(4, 10)
def part4 = isbn.substring(10, 12)
def part5 = isbn.substring(12, 13)
return "${part1}-${part2}-${part3}-${part4}-${part5}"
} else {
return isbn
}
}
You could first use the [] string operator to get the substrings instead of substring and drop the intermediate variables. For example in the case for length == 10:
"${isbn[0]}-${isbn[1..6]}-${isbn[7..8]}-${isbn[9]}"
Now, there is a bit of repetition there. You can get instead first get all the isbn segments and then .join them with '-':
[isbn[0], isbn[1..6], isbn[7..8], isbn[9]].join('-')
And, even further, instead of referencing isbn every time, you can make a list of the ranges you want to get and then get them all the same time using collect:
[0, 1..6, 7..8, 9].collect { isbn[it] }.join('-')
If you're going for code golfing, you can also do:
('-'+isbn)[1, 0, 2..7, 0, 8..9, 0, 10]
I'll leave it to you to figure out how that works, but i guess it's probably not a good idea to leave that on production code, unless you want to surprise future maintainers hehe.
Also, notice that the format when length == 13 is the same as for length == 10 but with a different prefix, you can then reuse the same function in that case. The whole function (with a couple of tests) would be:
/**
* 10 digit - #-######-##-#
* 13 digit - ###-#-######-##-#
**/
def formatIsbn(isbn) {
switch (isbn?.length()) {
case 10: return [0, 1..6, 7..8, 9].collect { isbn[it] }.join('-')
case 13: return isbn.take(3) + '-' + formatIsbn(isbn.drop(3))
default: return isbn
}
}
assert formatIsbn('abcdefghij') == 'a-bcdefg-hi-j'
assert formatIsbn('abcdefghijklm') == 'abc-d-efghij-kl-m'
Now, i think there are some bad smells in that code. Can isbn be null? At least to me, this doesn't look like a function that needs to bother about the nullity of its argument, or at least that's not clear by reading its name (it should be called something like formatIsbnOrNull instead if both ISBN strings and null values are accepted). If null values are not valid, then let it blow up with a NullPointerException when accessing isbn.length() so the caller know they have passed a wrong argument, instead of silently returning the same null.
The same goes for the return ISBN at the end. Is it expected for that function to receive a string that's neither 10 nor 13 characters long? If not, better throw new IllegalArgumentException() and let the caller know they have called it wrongly.
Finally, i'm not sure if this is the most "readable" solution. Another possible solution is having a string for the format, like '###-#-######-##-#' and then replace the #s by the isbn characters. I think it might be more self-documenting:
def formatIsbn(isbn) {
def format = [
10: '#-######-##-#',
13: '###-#-######-##-#'
][isbn.length()]
def n = 0
format.replaceAll(/#/) { isbn[n++] }
}
Consider adding the method to the String class, as shown here. Note that this answer is a spin on a clever suggestion in epidemian's answer (re: collect).
Note:
This code augments String with asIsbn().
The range [0..2] does not need the call to asIsbn(), but the symmetry of using collect twice is irresistable.
Groovy returns the last expression in if/else, so 'return' is not necessary
/**
* 10 digit - #-######-##-#
* 13 digit - ###-#-######-##-#
**/
String.metaClass.asIsbn = { ->
if (delegate.length() == 10) {
[0, 1..6, 7..8, 9].collect { delegate[it] }.join('-')
} else if (delegate.length() == 13) {
[0..2, 3..12].collect { delegate[it].asIsbn() }.join('-')
} else {
delegate
}
}
assert "abcdefghij".asIsbn() == 'a-bcdefg-hi-j'
assert "abcdefghijklm".asIsbn() == 'abc-d-efghij-kl-m'
assert "def".asIsbn() == "def"
String s = null
assert s?.asIsbn() == null
I would try using Regex... I think it's pretty much readable if you know how to use regex, and it's javascript inspired syntax in groovy is pretty cool also.
One more thing: it's pretty clear, looking at the capture groups, what your string looks like for the desired formatting.
private formatISBN(String isbn) {
if (isbn?.length() == 10) {
m = isbn =~ /(\d{1})(\d{6})(\d{2})(\d{1})/
return "${m[0][1]}-${m[0][2]}-${m[0][3]}-${m[0][4]}"
} else if (isbn?.length() == 13) {
m = isbn =~ /(\d{3})(\d{1})(\d{6})(\d{2})(\d{1})/
return "${m[0][1]}-${m[0][2]}-${m[0][3]}-${m[0][4]}-${m[0][5]}"
} else {
return isbn
}
}
Btw, #epidemian suggestion using backreferences is great! I think the code would look like:
private formatISBN(String isbn) {
if (isbn?.length() == 10) {
return isbn.replaceAll(/(\d{1})(\d{6})(\d{2})(\d{1})/, '$1-$2-$3-$4')
} else if (isbn?.length() == 13) {
return isbn.replaceAll(/(\d{3})(\d{1})(\d{6})(\d{2})(\d{1})/, '$1-$2-$3-$4-$5')
} else {
return isbn
}
}
Dunno if I like this any better. I'd make the position map a static final, too.
private isbnify(String isbn) {
def dashesAt = [ 10: [[0,1], [1,7], [7,9], [9,10]],
13: [[0,3], [3,4], [4,10], [10,12], [12,13]]]
def dashes = dashesAt[isbn?.length()]
(dashes == null) ? isbn
: dashes.collect { isbn.substring(*it) }.join('-')
}
Ranges make for a bit less clutter, IMO:
private isbnify3(String isbn) {
def dashesAt = [ 10: [0, 1..6, 7..8, 9],
13: [0..2, 3, 4..9, 10..11, 12]]
def dashes = dashesAt[isbn?.length()]
dashes == null ? isbn : dashes.collect { isbn[it] }.join("-")
}
With an inject-with-two-accumulators it should be easy to do a list-of-dash-positions version, too.
This should be a comment to #everton, but I don't have the 50 reputation needed to do that yet. So this answer is really just a suggested variation on #everton's answer.
One less regex by making the first 3 digits optional. The downside is having to remove a leading '-' if the ISBN is 10 characters. (I also prefer \d over \d{1}.)
private formatISBN(String isbn) {
String result = isbn.replaceAll(/^(\d{3})?(\d)(\d{6})(\d{2})(\d)$/,
'$1-$2-$3-$4-$5')
if (result) {
return result.startsWith('-') ? result[1..-1] : result
} else {
return isbn // return value unchanged, pattern didn't match
}
}
println formatISBN('1234567890')
println formatISBN('9991234567890')
println formatISBN('123456789') // test an ISBN that's too short
println formatISBN('12345678901234') // test an ISBN that's too long

Linq-Entities: Fetch data excluding the overlapping data ranges, pick the largest period

I have 2 tables, Imports and Periods.
Imports has the following structure:
AdminID, PeriodID, Some more fields
1, 1
1, 2
1, 6
1, 50
Periods table has the following structure:
PeriodID, PeriodType, StartDate, EndDate, Description
1, 1, 2007-01-01, 2007-12-31, Year 2007
2, 2, 2007-01-01, 2007-03-31, Quarter 1 2007
3, 2, 2007-04-01, 2007-06-30, Quarter 2 2007
4, 2, 2007-07-01, 2007-09-30, Quarter 3 2007
5, 2, 2007-10-01, 2007-12-31, Quarter 4 2007
6, 3, 2007-01-01, 2007-01-31, January 2007
.
.
.
50, 2, 2011-01-01, 2011-03-31, Quarter 1 2011
Now, I need to build a linq query to fetch only the largest period(ignoring the smaller overlapping periods) based on the data in Imports table!
When I query for AdminID = 1, I should only get PeriodID = 1 & 50, ignoring/excluding the PeriodIDs 2 & 6 as they overlap in 1 and 50 as there is no overlapping data yet!
You, can the max help for picking the largest period and while retrieving the values by comparing the PeriodIDs in both tables right.
I'm not sure whether there is a convenient way to do this in the database, but when you pull the data locally, you can do in-memory LINQ queries, if this is appropriate. You need to do this in thee steps.
Step 1: Define a Range class that allows you to do comparisons on periods (see below).
Step 2: Pulling the periods from the database:
var ranges = (
from period in context.Periods
where period.Imports.Any(i => i.AdminID == adminId)
select new Range(period.StartDate, period.EndDate.AddDays(1)))
.ToArray();
Note the .ToArray() to pull everything locally.
Step 3: Aggregating / merging all the periods into a list of non-overlapping periods:
var mergedPeriods = (
from range in ranges
select ranges.Where(p => p.OverlapsWith(range))
.Aggregate((r1, r2) => r1.Merge(r2)))
.Distinct();
For this to work you need a specially designed Range type that contains OverlapsWith, Merge and Equals methods. It might look like this:
public class Range : IEquatable<Range>
{
public Range(DateTime start, DateTime exclusiveEnd)
{
if (exclusiveEnd < start)
throw new ArgumentException();
this.Start = start; this.End = exclusiveEnd;
}
public DateTime Start { get; private set; }
public DateTime End { get; private set; }
public TimeSpan Duration { get { return this.End - this.Start; } }
public Range Merge(Range other)
{
if (!this.OverlapsWith(other)) throw new ArgumentException();
var start = this.Start < other.Start ? this.Start : other.Start;
var end = this.End > other.End ? this.End : other.End;
return new Range(start, end);
}
public bool Contains(Range other)
{
return this.Start <= other.Start && this.End > other.End;
}
public bool OverlapsWith(Range other)
{
return this.OverlapsOnStartWith(other) ||
other.OverlapsOnStartWith(this) ||
this.Contains(other) ||
other.Contains(this);
}
private bool OverlapsOnStartWith(Range other)
{
return this.Start >= other.Start && this.Start < other.End;
}
public bool Equals(Range other)
{
return this.Start == other.Start && this.End == other.End;
}
}
I hope this helps.
Well, after a long struggle, I did find an answer! With a single query to database!
And for everyone's benefit posting the same.
var oImportPeriods =
from o in Imports
where o.Administration.AdminID == 143
orderby o.Period.PeriodID
select o.Period;
var oIgnorePeriodList = (
from oPeriod in oImportPeriods
from oSearchPeriod in oImportPeriods
.Where(o => o.PeriodID != oPeriod.PeriodID)
where oPeriod.StartDate >= oSearchPeriod.StartDate
where oPeriod.EndDate <= oSearchPeriod.EndDate
select oPeriod.PeriodID)
.Distinct();
var oDeletablePeriods = oAdministrationPeriods
.Where(o => !oIgnorePeriodList.Contains(o.PeriodID));
foreach(var o in oDeletablePeriods)
Console.WriteLine(o.Name);

Resources