i'm struggling with an efficient way to store requirements for a course inside of a course.
e.g.:
You can take part at course A when you passed B
You can take part at course A too when you passed course C and D
I hoped to be able to use a tree structure so I can easily check if a Person passed all requirements but because of the multiple options to pass requirements it is not possible.
Do you know any technique or Data structure to solve this?
A Course object has a list of Prerequisite objects, only one of which needs to be fulfilled. A Prerequisite object has a list of required Course objects, all of which needs to be passed for the Prerequisite object to be fulfilled.
public class Course {
private List<Prerequisite> prerequisites;
public boolean canBeTakenBy(Student student) {
return prerequisites.isEmpty() ||
prerequisites.stream().anyMatch(p -> p.isFulfilledBy(student));
}
}
public class Prerequisite {
private List<Course> requiredCourses;
public boolean isFulfilledBy(Student student) {
return requiredCourses.stream().allMatch(student::hasPassed);
}
}
public class Student {
private Set<Course> passedCourses;
public boolean hasPassed(Course course) {
return passedCourses.contains(course);
}
}
There are many ways you can accomplish what you want, personally, I think the easiest is having an ArrayList< ArrayList < Course > >
So for example, if you want to add a possible requirement option, you could create an ArrayList with the courses and push it to the end of the requirements.
Later to check it, just run the combinations, complexity shouldn't be a problem, if you have for example 10 possible requirements with 10 courses each, worst case scenario it would be a 10x10 loop, 10² to the computer is the same as nothing at all.
https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html
Related
I have the following dataset, and am trying to work out how to best write a specific constraint.
My PlanningEntity looks (roughly) as follows:
#PlanningEntity
public class Participation {
#PlanningId
private long id;
private Student student;
private Lesson lesson;
#PlanningVariable(valueRangeProviderRefs = "possibleEnrollments")
private Boolean enrolled;
}
whereby a Lesson has a public List<Subject> getSubjects() (note: a list of multiple subjects).
What I would like to do in my penalize method is look at all participations of a student/subject (single subject!) combination. In other words, if I have lesson A with subject 1, lesson B with subject 2 and lesson C with subjects 1 and 2, I would like to do a grouping in such a way that in my penalize function I get two (Student, List<Participation>) callbacks: one for subject 1 and one for subject 2, whereby the first lists contains lessons A and C, and the second list has lessons B and C. So, C is contained in two lists.
The following does not work:
constraintFactory
.forEach(Participation.class)
.groupBy(Participation::getStudent, Participation::getSubjects, toList())
since this groups on the entire List returned by Participation::getSubject and the set 1 and 2 attached to lesson C becomes a separate group.
I have currently 'solved' the problem as follows, with a custom UniConstraintCollection:
return constraintFactory
.forEach(Participation.class)
.groupBy(Participation::getStudent, new UniConstraintCollector<Participation, Map<Subject, List<Participation>>, Map<Subject, List<Participation>>>() {
#Override
public Supplier<Map<Subject, List<Participation>>> supplier() {
return HashMap::new;
}
#Override
public BiFunction<Map<Subject, List<Participation>>, Participation, Runnable> accumulator() {
return (map, participation) -> {
for(Subject s : participation.getSubjects()) {
if(!map.containsKey(s)) {
map.put(s, new ArrayList<>());
}
map.get(s).add(participation);
}
return () -> {
for(Map.Entry<Subject, List<Participation>> entry: map.entrySet()) {
entry.getValue().remove(participation);
}
};
};
}
#Override
public Function<Map<Subject, List<Participation>>, Map<Subject, List<Participation>>> finisher() {
return Function.identity();
}
}).penalize(("name", HardSoftScore.ONE_SOFT, (student, participationMap) -> {
...
});)
This works, in that I receive a map of Subject to Participations and allows me to calculate the penalty I want.
However, this means that I calculate one penalty value for all subject/list combinations 'together'. From a usability perspective, I would like to penalize each subject/list separately. Is there a way to do this? (Maybe by rewriting the UniConstraintCollector to provide multiple lists of Participations, instead of one single Map<Subject, List<Participation>>?)
P.S. Another approach I have tried to achieve the same goal is to work from the perspective of the Subject. So, making Subjects a ProblemFact on the PlanningSolution, and working with something like
return constraintFactory
.forEach(Subject.class)
.join(constraintFactory.forEach(Participation.class),
JoinerSupport.getJoinerService().newBiJoiner(List::of, JoinerType.INTERSECTING, Participation::getSubjects)
)
I assume this is the way I would need to go, using the JoinerType INTERSECTING, but this gives me an "Unsupported Joiner Type" exception in AbstractLeftHandSide.
What if you start from Subject?
constraintFactory
.forEach(Subject.class)
.join(Participation.class,
// Joiners.containedBy() would do this far more efficient
filtering((s, p) -> p.getSubjects().contains(s))
.groupBy((s, p) -> s, toList((s, p) -> p))
This could be an expensive constraint performance wise. Benchmark it.
This isn't really an answer, but it might solve your problem too. It's too long to add as a comment.
The Boolean planning variable is typically an anti-pattern.
If you improve your model, the constraint might become easier to implement.
Instead, you could model it like this:
#PlanningEntity
public class Participation {
#PlanningId long id;
Student student;
#PlanningVariable(...) Lesson lesson;
}
This simpler model (on the right) will solve far more efficiently. But in this case, how many Participation instances should you create?
If the numbers of lessons for each student is fixed, that's obvious: that number per student.
If the numbers of lessons for each student is a planning decision, use overconstrainted planning: #PlanningVariable(nullable=true,...) Lesson lesson and create one participation for the maximum number of lessons of each student. This will allow the solver to leave some participation instances unassigned.
See also the Domain Modeling Guide in the docs.
So I have been having a go with using the method reference in Java 8 (Object::Method). What I am attempting to do, which I have done before but have forgotten (last time I used this method reference was about 4 months ago), is find the amount of players that != online using the Method Reference.
public static Set<Friend> getOnlineFriends(UUID playerUUID)
{
Set<Friend> friends = new HashSet<>(Arrays.asList(ZMFriends.getFriends(playerUUID)));
return friends.stream().filter(Friend::isOnline).collect(Collectors.toSet());
}
public static Set<Friend> getOfflineFriends(UUID playerUUID)
{
Set<Friend> friends = new HashSet<>(Arrays.asList(ZMFriends.getFriends(playerUUID)));
return friends.stream().filter(Friend::isOnline).collect(Collectors.toSet());
As you can see I managed to so it when the player (friend) is online but I cannot figure out how to filter though the Set and collect the offline players. I'm missing something obvious, but what is it?!?!
Thanks,
Duke.
In you code
public static Set<Friend> getOnlineFriends(UUID playerUUID)
{
Set<Friend> friends = new HashSet<>(Arrays.asList(ZMFriends.getFriends(playerUUID)));
return friends.stream().filter(Friend::isOnline).collect(Collectors.toSet());
}
you are creating a List view to the array returned by ZMFriends.getFriends(playerUUID), copy its contents to a HashSet, just to call stream() on it.
That’s a waste of resources, as the source type is irrelevant to the subsequent stream operation. You don’t need to have a Set source to get a Set result. So you can implement your operation simply as
public static Set<Friend> getOnlineFriends(UUID playerUUID)
{
return Arrays.stream(ZMFriends.getFriends(playerUUID))
.filter(Friend::isOnline).collect(Collectors.toSet());
}
Further, you should consider whether you really need both, getOnlineFriends and getOfflineFriends in your actual implementation. Creating utility methods in advance, just because you might need them, rarely pays off. See also “You aren’t gonna need it”.
But if you really need both operations, it’s still an unnecessary code duplication. Just consider:
public static Set<Friend> getFriends(UUID playerUUID, boolean online)
{
return Arrays.stream(ZMFriends.getFriends(playerUUID))
.filter(f -> f.isOnline()==online).collect(Collectors.toSet());
}
solving both tasks. It still wastes resource, if the application really needs both Sets, as the application still has to perform the same operation twice to get both Sets. Consider:
public static Map<Boolean,Set<Friend>> getOnlineFriends(UUID playerUUID)
{
return Arrays.stream(ZMFriends.getFriends(playerUUID))
.collect(Collectors.partitioningBy(Friend::isOnline, Collectors.toSet()));
}
This provides you both Sets at once, the online friends being associated to true, the offline friends being associated to false.
There are 2 ways I can think of:
friends.stream().filter(i -> !i.isOnline()).collect(Collectors.toSet());
But I guess that's not what you want, since it's not using a method reference. So maybe something like this:
public static <T> Predicate<T> negation(Predicate<T> predicate) {
return predicate.negate();
}
...
friends.stream().filter(negation(Friend::isOnline)).collect(Collectors.toSet());
I have been asked to model a foodstore that contains different types of food. I should be able to add a given quantity of a food type by using the addFood method and remove food using the takeFood method. The addFood must take the form addFood(String, int) and the takeFood must take the form takeFood(String), i.e. addFood("Steak", 5) would add 5 items of steak to the foodstore. I have attempted to make this class and wondered whether this meets what I have been tasked to do. For the sake of this example I will only use 2 food items but in reality there is much more.
public class Foodstore {
public void addFood(String food, int quantity) {
addFood("steak", quantity);
addFood("hay", quantity);
}
public void takeFood(String food) {
takeFood("Steak");
takeFood("hay");
}
}
Thanks in advance
Your food store is missing a warehouse
Map<String,Integer> warehouse = new HashMap<>();
and, as it is, when you add food you're ignoring the food you were told to add, not a good idea.
These are just some starting point, reorganize your code and explore your warehouse when you add data to make sure you're doing well.
I don't think so, you would need some kind of register where you actually save the information, like a Map:
Map<String,Integer> register = new HashMap<>();
You would have to rewrite your functions sth like this (have not been able to type it in an editor, but just so that you get the idea of it):
public void addFood(String food, int quantity) {
if (register.containsKey(food)) {
Integer newAmount = register.get(food) + quantity;
register.put(food,newAmount);
}
else {
register.put(food,quantity);
}
}
PS. You are (mis)using recursion, and I don't think it's what you want in your case. Your function addFood calls itself again and again without an end.
For example:
class Vehicle {
Collection<Axle> axles;
}
class Axle {
Collection<Wheel> wheels;
}
class Wheel {
// I think there are dually rims that take two tires -- just go with it
Collection<Tire> tires;
}
class Tire {
int width;
int diameter;
}
I have a service through which I can get a collection of all Vehicle objects I know about. Now say I have a tire of a specific width and diameter, and I want to find a Vehicle which can take it. The simplistic way is to have a set of four nested loops, like so:
for (Vehicle vehicle : vehicles) {
for (Axle axle : vehicle.getAxles()) {
for (Wheel wheel : axle.getWheels()) {
for (Tire tire : wheel.getTires()) {
if (tire.width == targetWidth
&& tire.diameter == targetDiameter) {
// do something
break;
}
}
}
}
}
Is there a good design pattern for this? Or a better data structure to use? Would it be better to just keep an index somewhere of tire information mapped to vehicles?
edit: answering questions from comments
Do you have control over the structure of the data you receive from the service?
Yes
Do you need to search for different tires multiple times in the same data?
Yes
Is performance an issue?
Not especially
When you find the tire, do you just need to know which vehicle contains it or do you also need the axle and wheel?
Sometimes just the vehicle, sometimes just the axle -- two different contexts
Do you need the reference to the tire that was found?
Yes, in the cases where I need the axle
edit2:
Extending the metaphor further, to explain the two contexts above:
Context 1 -- I want to know the vehicle, so I can send a worker out to collect the vehicle and bring it back
Context 2 -- I want to know the axle and tire, because I am at the vehicle trying to do the work
You could flatten out the loops by using Java 8 streams.
vehicles.stream()
.flatMap(vehicle -> vehicle.getAxles().stream())
.flatMap(axle -> axle.getWheels().stream())
.flatMap(wheel -> wheel.getTires().stream())
.filter(tire -> tire.width == targetWidth
&& tire.diameter == targetDiameter)
.forEach(tire -> {
// do something
});
The nice thing about streams is that you could insert additional filter, filter, findAny, etc., calls pretty easily anywhere in the sequence.
I would inverse your logic and move the question into the Vehicle, unless of course you'd like to keep your objects thin for any other reason (in which case I'd personally wrap them with a thicker object to add any behaviour needed)
class Vehicle {
...
public Tire acceptsTire(Tire tire) {
}
}
from here on there are several possibilities, depending on how important this piece of business logic is in your domain in general.
If you'll have several actions you could probably just iterate as you had done in your sample. Or possibly in the same way as I suggested, keep cascade the question to the correct component. As long as you can live with the time complexity of doing this that should be alright.
If this check is something you'd usually do then you could have a reference to the type of tires you hold in the vehicle directly, this could be either your Tire collection, or you could pass a TireSpecification instance when constructing the Vehicle if for any reason you need to keep these separated (Your intention is not very clear in the question, is the tire on the car or just an spec of what could fit?)
Without changing your data structure you won't be able to make significant difference. You can add some syntactic sugar with lambdas, but it is essentially the same solution.
Things you could look at:
Your model allows for Vehicles with zero axles or hundred. While it depends on your business model it seems to weird.
Your model allows to have different axles in your vehicle, different wheels. Is it really necessary? Make sure which elements of your model should have their separate identity (currently each object has it) and which is just a value object.
Make sure you really need such detailed model. Currently you have two classes (Axle,Wheel), which only hold collections of inner objects. If they will be just simple JavaBean object with getAllInnerTypes() then you should consider removal of this class. It may even be the case that tire information should be stored almost directly in Vehicle class.
As long as there aren't too many items and/or performance is not a big issue, I would probably just go with the nested loops (or streams from John's answer).
Since you have two contexts for the search, you could pass the appropriate action to the search method - something like this (using loops in this case):
interface TireAction {
void doSomething(Vehicle v, Axle a, Tire t);
}
void findTireAndPerform(int targetWidth, int targetDiameter, TireAction action) {
for (Vehicle vehicle : vehicles) {
for (Axle axle : vehicle.getAxles()) {
for (Wheel wheel : axle.getWheels()) {
for (Tire tire : wheel.getTires()) {
if (tire.width == targetWidth && tire.diameter == targetDiameter) {
action.doSomething(vehicle, axle, tire);
break;
}
}
}
}
}
}
void someMethod() {
...
findTireAndPerform(width, diameter, (v, a, t) -> {
// send worker to 'v'
});
...
findTireAndPerform(width, diameter, (v, a, t) -> {
// work on 'a' and 't'
});
}
I have created a large amount of People beans and was wanting to store them in some kind of data structure where I would be able to search for particular types of People beans (e.g. People beans with a last name of "Sanchez") as fast as possible (I don't want to use a DB by the way). Is the only way to loop over my beans and test currBean.getLastName().equals("Sanchez") for each bean?
I would like to be able to do something like the following:
List<PeopleBean> myPeople = myBeansDataStructure.getAll(new PeopleBean("John", "Sanchez", 36),
new Comparator<PeopleBean>() {
#Override
public int compare(PeopleBean b1, PeopleBean b2) {
// search conditions
}
});
and have it return a collection of beans matching the search. My searches will always be of the same 'kind', i.e., I will be either searching for beans with a particular last name, first name, or age (or some permutation of the three) so could something using an overridden equals method in the bean be used?
I am surprised this isnt there in the library.. or is it?
Anyway, you can write your own
public interface Condition<T> {
public bool satisfies(T t);
}
And write a generic searcher, which goes through the entire and applies this function to each of them and returns you a new of only the ones that return true.
You can use Java 8 (This is under the assumption that myBeansDataStructure is a Collection of some sort.):
List<PeopleBean> myPeople = myBeansDataStructure.stream().filter(person -> person.getLastName().equals("Sanchez")).collect(Collectors.toList());
Or you could try something like this:
List<PeopleBean> myPeople = myBeansDataStructure.stream().map(PeopleBean::getLastName).filter(lastName -> lastName.equals("Sanchez")).collect(Collectors.toList());
You can try this
List<PeopleBean> list=new ArrayList<>();
for(PeopleBean i:list){
if(i.getName().equals("whatEverName")){
//do something
}
}