Methods in Pharo - smalltalk

I am still learning Pharo, but it is a bit confusing. There two classes, CarRental and Car, and a Test class, CarRentalTest.
There are fixed number of cars, the same car cannot be rented twice, I have the code, but there is a mistake.
| carRental redPanda yellowPanda blackTesla |
carRental := CarRental new.
redPanda := Car panda.
yellowPanda := Car panda.
blackTesla := Car tesla.
carRental
addCar: redPanda;
addCar: yellowPanda;
addCar: blackTesla.
self assert: carRental availableCars size equals: 3.
carRental rent: redPanda days: 5.
self assert: carRental availableCars size equals: 2.
self assert: carRental rentedCars size equals: 1
I tried to initialize the availableCars and rentedCard methods, but there is still an issue.

You need to keep track of rented cars, right? To do that add the ivar rented to the CarRental class and initialize it to an empty collection:
rented := OrderedCollection new.
(in other words, include the line above in the #initialize method - instance side.)
Now, every time you rent a car add it to the rented collection:
rent: aCar
rented add: aCar
and when the car is returned
return: aCar
rented remove: aCar
Also you can add the getter method which was missing so far
rentedCars
^rented
and compute the cars available for rent as
availableCars
^cars copyWithoutAll: rented

Related

Does Optaplanner support "combinatorial" planning variables?

Example:
Students want to enroll in multiple courses of different course groups (Math, English, Spanish, History) and have issued preferences for each course group (ENG-1 > ENG-2 means course ENG-1 is preferred to course ENG-2).
Student A:
MATH-2 > MATH-4 > MATH-1 > ... > MATH-9
ENG-3 > ENG-4 > ENG-1 > ... > ENG-2
Student B:
ENG-1 > ENG-2 > ENG-4 > ... > ENG-3
SPA-4 > SPA-6 > SPA-3 > ... > SPA-2
HIST-1 > HIST-3 > HIST-2 ... > HIST-5
Student C:
...
Is it possible for a planning variable of each student (planning entity) to be a combination of each of their preferences? I.e. student A would be assigned MATH-2 and ENG-3, student B would be assigned ENG-1, SPA-4, and HIST-1, if the constraints allow for this.
Yes (and no). Technically no, because #PlanningVariable can only hold a single value.
But YES, OptaPlanner can handle your use case. You just need to choose the right way to map your domain to Java classes. You need to model a N:M relation between Student and Course:
Student A needs to enroll in 2 courses (one from the MATH group and one from the ENG group).
Student B needs to enroll in 3 courses (ENG, SPA, HIST).
etc.
You can model this type of relationship with the CourseAssignment class, which is your #PlanningEntity. It could look like this:
#PlanningEntity
class CourseAssignment {
final Student student; // e.g. Ann
final CourseGroup courseGroup; // e.g. MATH
#PlanningVariable(valueRangeProviderRefs = { "courseRange" })
Course course; // changed by Solver - could be MATH-1, MATH-2, ENG-1, HIST-...
}
Since the number of course assignments is known for each student and it's fixed, you'd simply create 2 CourseAssignment instances for Student A, 3 instances for Student B, etc.
Next, design your constraints to penalize each courseAssignment with a hard score penalty, if courseAssignment.course.group != courseAssignment.courseGroup and with a soft score penalty based on courseAssignment.student.getPreference(courseAssignment.course).

Optaplanner. School timetabling. Force first lession

I'm trying to add constraints to School timetabling example. For example: "all groups should have the first lesson".
I tried EasyScore and Streaming - no success. EasyScore cant finds a proper solution, shuffles lessons a lot. Streaming gave me an error: Undo for (Lesson(subj...)) does not exist
Code for Streaming:
from(Lesson::class.java)
.filter { it.timeslot != null }
.groupBy({ it.studentGroup }, { it.timeslot!!.day }, ConstraintCollectors.toList())
.filter { group, day, list ->
list.any { it.timeslot!!.number != 1 }
}
.penalize(
"Student must have first lesson",
HardSoftScore.ONE_HARD
) { group, day, list -> list.count { it.timeslot!!.number != 1 } },
Looks like I'm thinking the wrong direction.
https://github.com/Lewik/timetable
Any help will be greatly appreciated.
update: fixed == -> =!
As far as I understand it, I don't think you're enforcing what you intend to enforce. From what I make from your source code, you penalize every studentgroup's first lesson of the day.
What you should do to enforce the intended goal, is to penalize every studentgroup that does NOT have a timeslot with number == 1 but DOES have one (of the same day) where timeslot number != 1.
So something like :
join all Lesson.class instances with all Lesson.class instances where the first lesson's studentGroup equals the second lesson's studentGroup AND the first lesson's timeSlot's day equals the second lesson's timeSlot's day. You obtain a BiConstraintStream<Lesson, Lesson> this way...
from this, filter all Lesson.class instances where the first lesson's timeSlot's number is less than the second lesson's timeSlot number
then penalise the remaining where the first lesson's timeSlot number differs from 1. That equals penalising all of a studentGroup's days where they have some lesson that day without having any lesson that day during the first timeslot.
If I understood you correctly, that's what you wanted ?
I don't know the real source of the problem, but it's about hashCode. The exception was thrown because HashMap with Object key can't find by that Object.
Lesson class:
#Serializable
#NoArg
#PlanningEntity
data class Lesson(
val subject: String,
val teacher: String,
val studentGroup: String,
#PlanningVariable(valueRangeProviderRefs = ["timeslotRange"])
var timeslot: TimeSlot? = null,
#PlanningId
val id: String = UUID.randomUUID().toString(),
)
The implementation above will not work. It could be fixed if I remove data or add override fun hashCode() = Objects.hash(id). #PlanningId does not help here. Kotlin generates hashCode for data classes and seems it not working with optaplanner (or vise versa)
How about using .ifNotExists()?
First, convert student group from a String into a class and add #ProblemFactCollectionProperty List<StudentGroup> on your solution, then do
from(StudentGroup.class)
.ifNotExists(from(Lesson.class).filter(Lesson::isFirstTimeslot),
equals(this -> this, Lesson::getStudentGroup)
.penalize(...);

MutableList to MutableMap in Kotlin

I have a mutable list of objects that belong to custom class Expense.
Class Expense has following attributes:
amount
category
I want to create a mutable map by iterating through the list above and the result should be as follows:
category_1 : sum of all amounts that had category_1 as category
category_2 : sum of all amounts that had category_2 as category
...
I am looking for a one-liner if possible. Kotlin idiomatic code.
This is what I have so far:
listOfExpenses.associateTo(expensesByCategory) {it.category to it.amount}
I need the last part: it.amount to somehow be a sum of all the amounts belonging to a certain category.
listOfExpenses is the list of Expense objects, expensesByCategory is the map I want to modify
I know this is more than one line but it does what you need
val expensesByCategory = listOfExpenses
.groupBy { it.category }
.mapValues { it.value.sumBy { it.amount } }

Django get objects that are foreign key of two models

I have the following three models where Budget and Sale both contain a foreign key to Customer:
class Customer(models.Model):
name = models.CharField(max_length=45)
# ...
class Budget(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
# ...
class Sale(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
# ...
I want to get a queryset of all Customer objects for which both a Budget and Sale exists. I initially tried getting the intersection of the customer field of all Budget and Sale objects:
customers = {
budget.customer for budget in Budget.objects.all()
} & {
sale.customer for sale in Sale.objects.all()
}
This returns the correct objects, but becomes horribly inefficient as the size of my database grows.
How can I retrieve these objects in a more efficient way? Thanks for any help!
You can filter with:
Customer.objects.filter(
budget__isnull=False,
sale__isnull=False
).distinct()
Django can follow ForeignKeys in reverse. It uses the related_query_name=… parameter [Django-doc] for the name of relation. If that is not specified, it falls back on the related_name=… parameter [Django-doc] parameter, and if that is not specified, it will use the name of the model in lowercase, so budget and sale. We here make LEFT OUTER JOINs on the Budget and Sale table, and check if for both there is a non-null row. Likely the Django ORM will optimize this to INNER JOINs.

Find records with the condition from other model rails

I have Bank and Rating models. Bank has many ratings. Also, bank can be active and inactive (when it's license is suspended).
Inactive bank has date field (license_suspended) in DB with the date, when license was suspended. Active banks has nil in this field.
I need to find ratings only for active banks. I can find all banks with license_suspended: nil and then find associated rating with current date and add it one-by-one to array, but I think there is a better way to do it. I need something like this:
#ratings = Rating.where(date: Date.today.beginning_of_month, bank: bank.license_suspended.blank?)
Thanks!
class Bank < ActiveRecord::Base
has_many :ratings
scope :active, -> { where(license_suspended: nil) }
end
class Rating < ActiveRecord::Base
belongs_to :bank
end
I think this will do what you want:
Rating.joins(:bank).where(date: Date.today).where(bank: {license_suspended: nil})
Or this:
Rating.joins(:bank).where(date: Date.today).merge(Bank.active) //this way you reuse active scope from Bank model
This will result in the following query:
SELECT "ratings".* FROM "ratings" INNER JOIN "banks" ON "banks"."id" = "ratings"."bank_id" WHERE "ratings"."date" = 'today_date' AND banks.license_suspended IS NULL
Assuming Ratings belong to a Bank, this looks like it'll do what you want:
Rating.joins(:bank).where(date: Date.today.beginning_of_month, bank: {license_suspended: nil}