ENUM and Sorting in an arraylist - sorting

I wanna sort an array list which has name of days like ["Sat","Mon", "Wed", "Fri", "Sun"]. I wanna sort it by ENUM like:
public enum DayInWeek {
Sat, Sun, Mon, Tus, Wed, Thu, Fri
}
How can I do that?
Searching on web. could not find satisfying answer.
internal enum class WeekDayEnum {
Mon,
Tue,
Wed,
Thu,
Fri,
Sat,
Sun
}
inner class DayCompare(var days: WeekDayEnum) : Comparable<DayCompare> {
override fun compareTo(s1: DayCompare): Int {
return if (s1.days.ordinal < this.days.ordinal)
1
else if (s1.days.ordinal > this.days.ordinal)
-1
else 1
}
}

Comparing by ordinal is the default comparable behaviour of an enum. You can therefore sort your list like this:
val dayList = listOf("Sat","Mon", "Wed", "Fri", "Sun")
// Gives: [Mon, Wed, Fri, Sat, Sun]
val ordered = dayList.map(WeekDayEnum::valueOf).sorted()

Related

How to check if a date value inside an hash is bigger than a reference date

I'm writing a validation and I have an hash with this structure
elements.map{ |e| [e.id,e.coverable.published_at] }.to_h
=> {305=>Fri, 17 Apr 2020 15:23:00 CEST +02:00,
306=>Fri, 17 Apr 2020 13:00:00 CEST +02:00,
307=>Fri, 17 Apr 2020 09:20:00 CEST +02:00,
308=>Fri, 17 Apr 2020 12:59:00 CEST +02:00,
309=>Fri, 17 Apr 2020 11:39:00 CEST +02:00}
I have a reference date...
published_at
=> Mon, 04 May 2020 23:51:00 CEST +02:00
I have to check if any of the element has a published_at datetime value bigger than my published_at.
Is there a short way to do that?
Try something like this
elements.any? { |e| e.coverable.published_at > your_published_at }
In case you need the element which passes the condition use find
element = elements.find { |e| e.coverable.published_at > your_published_at }
# if element is not nil such element is present

Sort a collection with a property type Date value

I have a collection as:
content [Collection]
[0] [Content]
creationDate Thu Aug 22 11:50:37 GMT 2019
[1] [Content]
creationDate Thu Aug 22 11:45:37 GMT 2019
[2] [Content]
creationDate Thu Aug 22 11:54:37 GMT 2019
How can I sort this collection by date value?
i.e.
content [Collection]
[0] [Content]
creationDate Thu Aug 22 11:45:37 GMT 2019
[1] [Content]
creationDate Thu Aug 22 11:50:37 GMT 2019
[2] [Content]
creationDate Thu Aug 22 11:54:37 GMT 2019
Create a DateComparator for sorting.
class DateComparator implements Comparator<Date> {
public int compare(Date d1, Date d2) {
if (d1.before(d2)) {
return -1;
} else if (d1.after(d2)) {
return 1;
} else {
return 0;
}
}
}
and call content.sort(new DateComparator())

I can't get the 15th weekday in ruby

I cant get the next 15th day but not the working day.
DateTime.now.next_day(+15).strftime('%d %^B %Y')
how can i get the next 15th weekday?
You're just adding 15 days to the current date. What you want is to adjust the date:
date = DateTime.now
if (date.mday > 15)
date = date.next_month
end
date = date.next_day(15 - date.mday)
Where that adjusts to be the 15th of the next month if it's already past the 15th of the current month.
Now this can be extended to be an Enumerator:
def each_mday(mday, from: nil)
from ||= DateTime.now
Enumerator.new do |y|
loop do
if (from.mday > mday)
from = from.next_month
end
from = from.next_day(mday - from.mday)
y << from
from += 1
end
end
end
Which makes it possible to find the first day matching particular criteria, like being a weekday:
each_mday(15, from: Date.parse('2019-06-14')).find { |d| (1..5).include?(d.wday) }
Where that returns July 15th, as June 15th is a weekend.
The from argument is optional but useful for testing cases like this to ensure it's working correctly.
15.times.reduce(Date.civil 2019, 03, 24) do |acc, _|
begin
acc += 1
end while [0, 6].include? acc.wday
acc
end
#⇒ #<Date: 2019-04-12 ((2458586j,0s,0n),+0s,2299161j)>
So you want to add 15 business days from the current date. You can go with iGian or Aleksei vanilla ruby answers or use business_time gem:
15.business_days.from_now
If I understood correctly, you want to get next Monday if you hit Saturday or Sunday. Since wday gives you 0 for Sun and 6 for Sat, you can use it to as a conditional to add days towards Monday.
def date_add_next_week_day(days)
date = (Date.today + days)
date += 1 if date.wday == 6
date += 1 if date.wday == 0
date
end
date_add_next_week_day(15).strftime('%d %^B %Y')
If I get the point you need to find the 15th day after a specified date, skipping weekends.
One possible option is to define the skipping_weekend hash like this, considering Date.html#wday:
skip_weekend = { 6 => 2, 0 => 1}
skip_weekend.default = 0
Then:
next15 = DateTime.now.next_day(15)
next15_working = next15.next_day(skip_weekend[next15.wday]).strftime('%d %B %Y')
Now if next15 falls on a working day, next15_working is the same day (hash defaults to 0), otherwise it skips 2 days if Saturday (6th week day, hash maps to 2) or 1 day if Sunday (0th week day, hash maps to 1)
I assume that, given a starting date, ds (a Date object), and a positive integer n, the problem is determine a later date, dt, such that between ds+1 and dt, inclusive, there n weekdays.
require 'date'
def given_date_plus_week_days(dt, week_days)
wday = dt.wday
weeks, days = (week_days + {0=>4, 6=>4}.fetch(wday, wday-1)).divmod(5)
dt - (wday.zero? ? 6 : (wday - 1)) + 7*weeks + days
end
The variable wday is assigned to the day of week for the start date, dt. The start date is moved back to the previous Monday, unless it falls on a Monday, in which case it is not changed. That is reflected in the expression
wday.zero? ? 6 : (wday - 1)
which is subtracted from dt. The number of week days is correspondingly adjusted to
week_days + { 0=>4, 6=>4 }.fetch(wday, wday-1)
The remaining calculations are straightforward.
def display(start_str, week_days)
start = Date.parse(start_str)
7.times.map { |i| start + i }.each do |ds|
de = given_date_plus_week_days(ds, week_days)
puts "#{ds.strftime("%a, %b %d, %Y")} + #{week_days} -> #{de.strftime("%a, %b %d, %Y")}"
end
end
display("April 8", 15)
Mon, Apr 08, 2019 + 15 -> Mon, Apr 29, 2019
Tue, Apr 09, 2019 + 15 -> Tue, Apr 30, 2019
Wed, Apr 10, 2019 + 15 -> Wed, May 01, 2019
Thu, Apr 11, 2019 + 15 -> Thu, May 02, 2019
Fri, Apr 12, 2019 + 15 -> Fri, May 03, 2019
Sat, Apr 13, 2019 + 15 -> Fri, May 03, 2019
Sun, Apr 14, 2019 + 15 -> Fri, May 03, 2019
display("April 8", 17)
Mon, Apr 08, 2019 + 17 -> Wed, May 01, 2019
Tue, Apr 09, 2019 + 17 -> Thu, May 02, 2019
Wed, Apr 10, 2019 + 17 -> Fri, May 03, 2019
Thu, Apr 11, 2019 + 17 -> Mon, May 06, 2019
Fri, Apr 12, 2019 + 17 -> Tue, May 07, 2019
Sat, Apr 13, 2019 + 17 -> Tue, May 07, 2019
Sun, Apr 14, 2019 + 17 -> Tue, May 07, 2019

Ruby: Grouping a date range by year and month

I am creating a range for each month in my example range.
example_range = (Time.zone.today..2.years.from_now)
Output should look like so:
=> [Wed, 03 Aug 2016..Wed, 31 Aug 2016, Thu, 01 Sep 2016..Fri,
30 Sep 2016, Sat, 01 Oct 2016..Mon, 03 Oct 2016, ...]
At the moment I'm doing this, which doesn't work for ranges longer than a year, because the grouping will put January '16 and January '17 in one group.
example_range.group_by(&:month).each { |_, month| month.first..month.last }
I also tried this, but ruby segfaults on this for some reason...
example_range.group_by(&:year).map{ |ary| ary.group_by(&:month)}
Does anyone know a more beautiful (or at least working) way of doing this?
How is this:
example_range.group_by {|date| [date.year, date.month] }.map {|_, month| month.first..month.last }
If you are using Active Support (Rails), this will also work:
example_range.group_by(&:beginning_of_month).map {|_, month| month.first..month.last }
The best solution I think is this:
example_range.group_by {|date| date.month.to_s + "-" + date.year.to_s}
You can adjust the way you need.

Comparing elements inside array of ranges

So given that I have this array of ranges:
[
[0] Mon, 29 Dec 2014 07:30:00 PST -08:00..Mon, 29 Dec 2014 10:59:59 PST -08:00,
[1] Mon, 29 Dec 2014 12:30:01 PST -08:00..Mon, 29 Dec 2014 15:00:00 PST -08:00,
[2] Mon, 29 Dec 2014 07:30:00 PST -08:00..Mon, 29 Dec 2014 08:59:59 PST -08:00,
[3] Mon, 29 Dec 2014 10:30:01 PST -08:00..Mon, 29 Dec 2014 15:00:00 PST -08:00
]
How do I compare ranges that have the same minimum value and remove that element if the maximum value is greater than the other?
Two ways, where a is the array of ranges:
#1
a.each_with_object({}) { |r,h| h.update({ r.first=>r }) { |_,ov,nv|
[ov,nv].min_by(&:last) } }.values
#2
a.group_by(&:first).values.map { |r| r.min_by(&:last) }
Admittedly, this will be slow:
your_array.group_by do |range|
range.min
end.each do |min_value, ranges|
least_max = ranges.map(&:max).min
ranges.delete_if{ |range| range.max != least_max }
end.values
The following might be faster and also will delete things from your original array:
min_maxes = {}
your_array.each do |range|
min = range.min
max = range.max
if min_maxes[min].nil? || (min_maxes[min] > max)
min_maxes[min] = max
end
end
your_array.delete_if do |range|
min_maxes[range.min] < range.max
end
If there is multimap data structure we can handle this scenario easily. It is a hashing implementation using binary tree and elements are ordered in keys. And it allows duplicate keys. It is there in C++, not sure is there anything similar in Ruby. Since question tagged 'data structure' hopefully my answer spread some lights.
For your case, you can consider lower range as key and upper range as value. If there is a collision in lower range you can easily identify that and compare the values of collided records and delete one if necessary.

Resources