How to get all the possible matches of two lists? - java

For example, I have a list which contains some Lecture instances, each lecture has a certain number of students attending this lecture and another list which contains some Classroom instances, each classroom has a maximum capacity.
Now I intend to assign each lecture in lecture list with a classroom in classroom list, all lectures in lecture class should have a classroom, then create a map to store this possibility.
And I want to return all these possible matches in the form of a set.
For example:
Classroom List: [Classroom1(50),Classroom2(70),Classroom3(80)]
Lecture list: [Lecture1(50), Lecture2(70), Lecture3(50)]
Then we have 3 possible maps, which are:
{lecture1:classroom1, lecture2:classroom2, lecture3:classroom3} and
{lecture1:classroom1, lecture2:classroom3, lecture3:classroom2} and
{lecture1:classroom2, lecture2:classroom3, lecture3:classroom1}
After that, all possible maps should be stored in a set.
I am new to programming and has not learned algorithm yet, maybe that's why I'm so struggled on this, I'd be grateful if someone could help me solve this problem.

What you seem to be after is something known as the cartesian product.
See https://en.wikipedia.org/wiki/Cartesian_product
You can do this with Java 8 streams
All permutations
// Just substitute the types and values for Lecture and Classroom instances
// I'm not going to do this for you
final List<String> first = Arrays.asList("foo","bar","baz");
final List<String> second = Arrays.asList("spam","ham","eggs");
final Set<Map.Entry<String,String>> objects =
first
.stream()
.flatMap(f ->
second
.stream()
.map(s -> new AbstractMap.SimpleEntry<>(f, s)))
.collect(Collectors.toSet());
Your 'objects' set is going to contain Abstract entry maps which hold your combinations.
Set[
Map{foo : spam}
Map{foo : ham}
Map{foo : eggs}
Map{bar : spam}
Map{bar : ham}
Map{bar : eggs}
Map{baz : spam}
Map{baz : ham}
Map{baz : eggs}
]
Groups of combinations
If you actually want 3 items in your set, you can do an intermediate collect on the second stream to collect into a data structure of your choice. Below shows that for a list, as I've already shown use of Collectors.toSet()
final Set<List<AbstractMap.SimpleEntry<String,String>>> objects =
first
.stream()
.map(f ->
second
.stream()
.map(s -> new AbstractMap.SimpleEntry<>(f, s))
.collect(Collectors.toList()))
.collect(Collectors.toSet());
Your 'objects' set is going to contain a list of Abstract entry maps which hold your combinations.
Set[
List(
Map{foo : spam}, Map{foo : ham}, Map{foo : eggs}
),
List(
Map{bar : spam}, Map{bar : ham}, Map{bar : eggs}
),
List(
Map{baz : spam}, Map{baz : ham}, Map{baz : eggs}
)
]
This illustrates a simple cartesian product algorithm using Java 8 in a single functional statement. Should you wish to add any clauses or exclusions, you can use filter or any of the other higher order functions to manipulate the stream.

So i got sucked in to this one and wrote a working solution
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
class ClassMatcher {
//The set of all possible matchings.
static ArrayList<ArrayList<Pair>> set = new ArrayList<ArrayList<Pair>>();
// The current matching being built
static ArrayList<Pair> cur = new ArrayList<Pair>();
public static void main(String[] args) {
Lecture[] l = { new Lecture(50, 1), new Lecture(70, 2), new Lecture(50, 3)};
ArrayList<Classroom> c = new ArrayList<>(Arrays.asList(
new Classroom(50, 1), new Classroom(70, 2),
new Classroom(100, 3)));
for (int i = 0; i < l.length; i++) {
//Fill with dummy values
cur.add(new Pair(new Classroom(-1, -1), new Lecture(-1, -1)));
}
// Sort the arrays to save work in rec()
Arrays.sort(l);
//Sort classrooms in descending order
Collections.sort(c, new Comparator<Classroom>() {
#Override
public int compare(Classroom o1, Classroom o2) {
return o1.compareTo(o2) * -1;
}
});
recursive(l, c, 0);
// Print all the sets
for (int i = 0; i < set.size(); i++) {
System.out.print("{");
for (int j = 0; j < set.get(i).size(); j++) {
System.out.print("Lecture " + set.get(i).get(j).l + ": "
+ "Classroom " + set.get(i).get(j).c);
if (j < set.get(i).size() - 1) {
System.out.print(", ");
} else {
System.out.print("}");
}
}
System.out.println();
}
}
public static void recursive(Lecture[] lectureList,
ArrayList<Classroom> classroomList, int curLecture) {
for (int i = 0; i < classroomList.size(); i++) {
// if the classroom is smaller than the lecture we cna stop as the
// lists are sorted so all other lectures will be to big for the
// current classroom
if (lectureList[curLecture].size > classroomList.get(i).size) {
return;
}
//Match the current classroom to the current lecture and add to the working matching
cur.set(curLecture, new Pair(classroomList.get(i), lectureList[curLecture]));
//If there are more lectures to do then remove the used classroom and recursively call.
if (curLecture < lectureList.length - 1) {
Classroom tmp = classroomList.remove(i);
recursive(lectureList, classroomList, curLecture + 1);
classroomList.add(i, tmp);
}
// If no Lectures left then add this matching to the set of all matchings.
else {
ArrayList<Pair> copy = (ArrayList<Pair>) cur.clone();
set.add(copy);
}
}
}
}
class Classroom implements Comparable<Classroom> {
int size;
int number;
public Classroom(int s, int n) {
size = s;
number = n;
}
#Override
public int compareTo(Classroom o) {
return Integer.compare(this.size, o.size);
}
public String toString() {
return number + " (" + size + ")";
}
}
class Lecture implements Comparable<Lecture> {
int size;
int number;
public Lecture(int s, int n) {
size = s;
number = n;
}
#Override
public int compareTo(Lecture o) {
return Integer.compare(this.size, o.size);
}
public String toString() {
return number + " (" + size + ")";
}
}
class Pair {
Classroom c;
Lecture l;
public Pair(Classroom c, Lecture l) {
this.c = c;
this.l = l;
}
}
This gives the output
{Lecture 1 (50): Classroom 3 (100), Lecture 3 (50): Classroom 1 (50), Lecture 2 (70): Classroom 2 (70)}
{Lecture 1 (50): Classroom 2 (70), Lecture 3 (50): Classroom 1 (50), Lecture 2 (70): Classroom 3 (100)}
{Lecture 1 (50): Classroom 1 (50), Lecture 3 (50): Classroom 3 (100), Lecture 2 (70): Classroom 2 (70)}
{Lecture 1 (50): Classroom 1 (50), Lecture 3 (50): Classroom 2 (70), Lecture 2 (70): Classroom 3 (100)}

Code below will give you all the matches, you can use them like whatever you want
HasMap<Integer, Integer> match = new HashMap<Integer, Integer>();
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
if(classroom[i] >= lecture[j]) {
match.add(lecture[j], classroom[i]);
}
}
}
if you want seperate maps like for every classroom or lecture you can try this(example for classrooms)
HashMap<Integer, Integer> classroom1 = new HashMap<Integer, Integer>();
HashMap<Integer, Integer> classroom2 = new HashMap<Integer, Integer>();
HashMap<Integer, Integer> classroom3 = new HashMap<Integer, Integer>();
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
if(i == 0) {
if(classroom[i] >= lecture[j]) {
classroom1.add(lecture[j], classroom[i]);
}
}
if(i == 1) {
if(classroom[i] >= lecture[j]) {
classroom2.add(lecture[j], classroom[i]);
}
}
if(i == 2) {
if(classroom[i] >= lecture[j]) {
classroom3.add(lecture[j], classroom[i]);
}
}
}
}
After this you can create map of maps. Don't mind about correcting me or adding something. Have a good day!

The algorithm in itself might look something like this using an nested for each loop:
public Set<Object[]> allocateRooms(List<Lecture> lectures, List<ClassRoom> classRooms){
Set<Object[]> returnSet = new LinkedHashSet<>();
for(Lecture l: lectures){
for (ClassRoom c: classRooms){
Object[] n = new Object[2];
n[0] = c;
n[1] = l;
returnSet.add(n);
}
}
return returnSet;
}

Here is an answer using Maps instead of arrays:
public Set<Map<Lecture, ClassRoom>> allocateRooms(List<Lecture> lectures, List<ClassRoom> classRooms){
Set<Map<Lecture, ClassRoom>> returnSet = new LinkedHashSet<>();
for(Lecture l: lectures){
for (ClassRoom c: classRooms){
Map<Lecture, ClassRoom> n = new HashMap<>();
n.put(l,c);
}
}
return returnSet;
}

Here is an example with sorted data collections:
public Set<Map<Lecture, ClassRoom>> allocateRooms(List<Lecture> lectures, List<ClassRoom> classRooms){
List<ClassRoom> sortedClassRooms = classRooms
.stream().sorted(Comparator.comparingInt(a -> a.getId())).collect(Collectors.toList());
List<Lecture> sortedLectures = lectures
.stream().sorted(Comparator.comparingInt(a -> a.getId())).collect(Collectors.toList());
Set<Map<Lecture, ClassRoom>> returnSet = new LinkedHashSet<>();
for(Lecture l: sortedLectures){
for (ClassRoom c: sortedClassRooms){
Map<Lecture, ClassRoom> n = new HashMap<>();
n.put(l,c);
}
}
return returnSet;
}

Related

Combine values of a list of data that are almost equal

so i asked before but it seems i wasnt clear enough of what im talking about, so im trying to make it clearer now:
what im trying to do is prepare data for an import. the data i get is human made an not very efficient, so im removing unnecessary entrys and try to combine the data as much as possible.
its for something like a configurator. the data i get looks something like this:
123 : 45 : AB = 12
This means: if Option 1 is 1 OR 2 OR 3 and Option 2 is 4 OR 5 and Option 3 is A OR B the result will be 1 AND 2
i created a class thats something like this:
Class Options{
String opt1;
String opt2;
String opt3;
String optResult;
//and some other stuff
boolean hasSameOptions(Options o){
return opt1.equals(o.opt1) && opt2.equals(o.opt2) && opt3.equals(o.opt3);
}
public void AddOptions(String options) {
for (String s : options.split("")) {
if (!optResult.contains(s)) {
optResult = optResult + s;
}
}
}
}
now, the data is repetitive and can be combined. Like:
12 : 45 : AB = 12
12 : 45 : AB = 3
12 : 45 : AB = 4
This would mean actually mean: 12 : 45 : AB = 1234
So, what i do is break the Strings apart to get only single values with the result, for example:
1 : 4 : A = 12
1 : 4 : B = 12
1 : 5 : A = 12
//and so on.
I make a list of all these Values and then try to Combine them again to get more efficient List.
The first step i do is get all Objects who have the same Options but different Results and combine the results. that happens like this:
public static List<Options> cleanList(List<Options> oldList) {
List<Options> newList = new ArrayList<>();
for (Options item : oldList) {
Options temp = findEqualOptions(newList, item);
if (temp != null)
temp.AddOptions(item.optResult);
else
newList.add(item);
}
return newList;
}
public static <T> T findByProperty(Collection<T> col, Predicate<T> filter) {
return col.stream().filter(Objects::nonNull).filter(filter).findFirst().orElse(null);
}
public static Options findEqualOptions(List<Options> list, Options opt) {
return findByProperty(list, d -> d.hasSameOptions(opt));
}
After that, i try to compress the list even more, by combining elements who have only ONE different value. For example:
1 : 2 : A = 12
1 : 3 : A = 12
-> 1 : 23 : A = 12
i do it like this:
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
Option o1 = list.get(i);
Option o2 = list.get(j);
int diff1 = 0;
int diff2 = 0;
int diff3 = 0;
int diff4 = 0;
if(!o1.opt1.equals(o2.opt1))
diff1 = 1;
if(!o1.opt2.equals(o2.opt2))
diff2 = 1;
//and so on
if((diff1+diff2+diff3+diff4)>1)
continue;
if(diff1 == 1)
o1.opt1 = o1.opt1 + o2.opt1;
//and so on...
list.remove(j--);
}
}
i do this until there are no more changes. It works well, but slowly. especially the method cleanList().
does anybody have any idea how to make it better? i tried to use a stream to get the whole list of equals options directly like this:
public static <T> List<T> findByMultipleValue(Collection<T> col, Predicate<T> filter) {
return col.stream().filter(filter).collect(Collectors.toList());
}
public static List<Options> getEqualOptionsList(List<Options> optList, Options opt){
return findByMultipleValue(optList, o -> o.hasSameOptions(opt));
}
but that made it A LOT slower.
PS. : its not the complete code, just an example of what im trying to do. I hope it is more understandable this time :)
probably not the most elegant or optimal solution but here is already a quick approach that give the result based on your description. It use the HashMap as proposed in the comment of #Joseph Larson
I went for a set of char to ensure values are not duplicate in it but feel free to adapt :)
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
class Scratch {
public static class Option{
String opt1;
String opt2;
String opt3;
String optResult;
public Option(String opt1, String opt2, String opt3, String optResult) {
this.opt1 = opt1;
this.opt2 = opt2;
this.opt3 = opt3;
this.optResult = optResult;
}
public static String merge(String a, String b){
StringBuilder value = new StringBuilder();
Set<Character> result = new HashSet<>();
for(char c : a.toCharArray()){
result.add(c);
}
for(char c : b.toCharArray()){
result.add(c);
}
for(char c : result){
value.append(c);
}
return value.toString();
}
public Option(Option a, Option b) {
this(merge(a.opt1, b.opt1), merge(a.opt2, b.opt2), merge(a.opt3, b.opt3), merge(a.optResult, b.optResult));
}
String getKey(){
return String.join(":", opt1, opt2, opt3);
}
int distance(Option option){
int diff1 = this.opt1.equals(option.opt1)?0:1;
int diff2 = this.opt2.equals(option.opt2)?0:1;
int diff3 = this.opt3.equals(option.opt3)?0:1;
int diff4 = this.optResult.equals(option.optResult)?0:1;
return diff1 + diff2 + diff3 + diff4;
}
public String toString(){
return getKey();
}
}
public static void main(String[] args) {
Option[] data = new Option[]{
new Option("12", "45", "AB", "12"),
new Option("12", "45", "AB", "3"),
new Option("12", "45", "AB", "4"),
new Option("12", "45", "AC", "1"),
new Option("12", "45", "AC", "12"),
new Option("3", "45", "AC", "13"),
new Option("12", "45", "AD", "12"),
};
mergeExact(data);
mergeClose(data, 1);
}
private static void mergeClose(Scratch.Option[] data, int distance){
Map<Option, Set<Character>> buffer = new HashMap<>();
for(Option option : data) {
boolean found = false;
Option toDelete = null;
for(Map.Entry<Option, Set<Character>> entry : buffer.entrySet()){
if(option.distance(entry.getKey()) <= distance){
Option merged = new Option(entry.getKey(), option);
for(char c : option.optResult.toCharArray()){
entry.getValue().add(c);
}
buffer.put(merged, entry.getValue());
toDelete = entry.getKey();
found = true;
break;
}
}
if(found) {
buffer.remove(toDelete);
}else{
Set<Character> set = new HashSet<>();
for(char c : option.optResult.toCharArray()){
set.add(c);
}
buffer.put(option, set);
}
}
System.out.println(String.format("merge with distance of %d:: %s", distance, buffer));
}
private static void mergeExact(Scratch.Option[] data) {
Map<String, Set<Character>> buffer = new HashMap<>();
for(Option option : data){
Set<Character> item = buffer.computeIfAbsent(option.getKey(), k -> new HashSet<>());
for(char c : option.optResult.toCharArray()){
item.add(c);
}
}
System.out.println("exact merge:: "+buffer);
}
}
output is
exact merge:: {3:45:AC=[1, 3], 12:45:AD=[1, 2], 12:45:AC=[1, 2], 12:45:AB=[1, 2, 3, 4]}
merge with distance of 1:: {12:45:AB=[1, 2, 3, 4], 3:45:AC=[1, 3], 12:45:ACD=[1, 2]}
EDIT: missed a part of the question, updating to add the merge when difference is close. This part is probably even worst that the first one in terms of optimisation but it's a working bases :)

Sorting algortithm with index in java

I have a set of values for srting algorithm. I have successfully sorted them out. But I also want to have the index for each element after sorting. For example like :
Array = [95, 53, 24, 10]
Output after sorting should be like :
10 at index 3, 24 at index 2, 53 at index 1 and 95 at index 0
I have used the following logic for sorting. But unable to get the indexes
for (int p = 0; p < ((list.size()) - 1); p++) {
int min = p;
count++;
for(int q=p+1; q<list.size();q++) {
if(doubleArray[q] < doubleArray[min]) {
min = q;
}
}
double smallNumber = doubleArray[p];
doubleArray[p] = doubleArray[min];
doubleArray[min] = smallNumber;
}
As this is probably homework, just some ideas:
before sorting, create a copy of your initial array
after sorting, iterate the original array, and then find the index of each value in the sorted array, and print that
the tricky part is dealing with values that show up repeatedly. but that is something that depends on your exact requirements.
Alternatively, you could look into introducing a helpful data structure, such as a Pair<Integer, Integer> class. The first entry represents the value, the second one an index. Then you can define your own "sorting" on that class.
As previously suggested, I would also recommend using an additional Item class which stores the item on which you want to sort and the initial index:
public class Item<T extends Comparable<T>> implements Comparable<Item<T>> {
public final T item;
public final int index;
public Item(T item, int index) {
if (item == null)
throw new NullPointerException("the given item is null!");
this.item = item;
this.index = index;
}
#Override
public int compareTo(Item<T> t) {
if (t == null)
return 1;
return item.compareTo(t.item);
}
}
When you need to sort the array of doubles, you first create an ArrayList containing the Items which store the doubles of the input array and the initial index. Since the Item class implements the Comparable interface, you can use Collections.sort for sorting (which will be faster than your bubblesort implementation):
public static void sort(Integer... array) {
List<Item<Integer>> copy = new ArrayList<Item<Integer>>(array.length);
// copy the input array
for (int i = 0; i < array.length; ++i)
copy.add(new Item<Integer>(array[i], i));
Collections.sort(copy);
for (Item<Integer> t : copy)
System.out.println(t.item + " at index " + t.index);
}
Try this:
Create a Pair class like
class Pair {
int val;
int index;
}
sort it by valuez
index will keep the initial index
I would suggest below approach:
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
public class trial
{
public static void main(String[] args)
{
List<Integer> aList = Arrays.asList(95, 53, 24, 10);
Map<Integer, Integer> aMap = new HashMap<>();
int index = 0;
for( Integer aInteger : aList )
{
aMap.put(aInteger, index);
index++;
}
SortedSet<Integer> keys = new TreeSet<>(aMap.keySet());
for( Integer key : keys )
{
Integer value = aMap.get(key);
System.out.println(key + " at index " + value);
}
}
}
Here you find the old index and shorted value
Map<Integer, Integer> map1 = numbers.stream().collect(Collectors.toMap(i -> i, i -> numbers.indexOf(i))). entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
//output {10=3, 24=2, 53=1, 95=0}
I have just tried the following and it worked.
int[] index = {0,1,2,3}
for (int p=0;p<((list.size())-1);p++)
{
int min = p;
count++;
for(int q=p+1; q<list.size();q++)
{
if(doubleArray[q]< doubleArray[min])
{
min = q;
}
}
double smallNumber = doubleArray[p];
doubleArray[p] = doubleArray[min];
doubleArray[min] = smallNumber;
store = index[p];
index[p] = index[min];
index[min] = store;
}
}
and it worked.

Extract the item from the list which has highest number of matches from given String

I have 2 Arraylists in my app. First arraylist is of Object type which contains a list of questions. Now this list of questions have a field named "Keywords". This is a String but can contain comma separated keywords.
Now I have a text field where user can search question based on these keywords.Issue that I am facing is that I want to filter out question from the question list according to the number of keyword matches.
For eg. User entered 3 comma separated keywords in the search text field. What I want now is if all 3 keyword matches with some value in the question list then I have to return those elements only. This part is easy and I can do it.
But if we don't get any exact match in the list, then I have to find that item which has the maximum keyword match i.e. if 2 out of 3 keywords from the comma separated String matches from some item in the list, then I have to return that item as result.
Value Stored in List :-
a) Hi, Hello, Hola, Bonjour.
b) Hi, Hello
c) Hi
Value entered in the search text :-
Hi, Hello, Hola
Now in response I want only the first element as it has 3 keywords matching from what user entered.
I am unable to figure out how to do this. Moreover I am fetching this questions list from sqlite database, so if this can be done with some sql queries then I am ready for that thing too.
This is my current code for filter method
public ArrayList<QuestionAnswerModal> filter(String keyword,boolean isQuestionSearch) {
ArrayList<QuestionAnswerModal> arrayList = new ArrayList<>();
if (!isQuestionSearch) {
for (QuestionAnswerModal modal : questionAnswerArrayList) {
if (modal.getKeyword().equalsIgnoreCase(keyword)) {
arrayList.add(modal);
}else{
ArrayList<String> keywords=new ArrayList<>();
String[]word=modal.getKeyword().split(",");
}
}
if (arrayList.size() > 0) {
System.out.print("list size "+arrayList.size());
} else {
System.out.print("No records found");
}
return arrayList;
}else{
for (QuestionAnswerModal modal : questionAnswerArrayList) {
if (modal.getQuestion().equalsIgnoreCase(keyword)) {
arrayList.add(modal);
}
}
if (arrayList.size() > 0) {
System.out.print("list size "+arrayList.size());
} else {
System.out.print("No records found");
}
return arrayList;
}
}
I leave it to you as an exercise to figure out how this solution works, but feel free to ask any questions you wish.
Java 7 solution:
import java.util.*;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;
public class MaxMatchFinder {
public static void main(String[] args) {
Map<String, Set<String>> tagsByName = new HashMap<>();
tagsByName.put("a", new HashSet<>(Arrays.asList("Hi", "Hello", "Hola", "Bonjour")));
tagsByName.put("b", new HashSet<>(Arrays.asList("Hi", "Hello")));
tagsByName.put("c", new HashSet<>(Arrays.asList("Hi")));
String searchText = "Hi, Hello, Hola";
String[] tagsToFind = searchText.split(",");
Map<String, Integer> matchCountsByEntryName = new HashMap<>();
for (String tagToFind : tagsToFind) {
for (String entryName : tagsByName.keySet()) {
Set<String> tags = tagsByName.get(entryName);
if (tags.contains(trimToEmpty(tagToFind))) {
Integer count = matchCountsByEntryName.get(entryName);
Integer incrementedCount = count == null ? 1 : count + 1;
matchCountsByEntryName.put(entryName, incrementedCount);
}
}
}
List<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(matchCountsByEntryName.entrySet());
Collections.sort(sortedEntries, new Comparator<Map.Entry<String, Integer>>() {
#Override
public int compare(Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) {
return e2.getValue().compareTo(e1.getValue());
}
});
Map.Entry<String, Integer> entryWithMostMatches = sortedEntries.get(0);
System.out.printf("Of the entries to be searched," +
" entry \"%s\" contains the most matches (%d).\n",
entryWithMostMatches.getKey(), entryWithMostMatches.getValue());
}
}
Java 8 solution:
import java.util.*;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;
public class MaxMatchFinder {
public static void main(String[] args) {
Map<String, Set<String>> tagsByName = new HashMap<>();
tagsByName.put("a", new HashSet<>(Arrays.asList("Hi", "Hello", "Hola", "Bonjour")));
tagsByName.put("b", new HashSet<>(Arrays.asList("Hi", "Hello")));
tagsByName.put("c", new HashSet<>(Arrays.asList("Hi")));
String searchText = "Hi, Hello, Hola";
String[] tagsToFind = searchText.split(",");
Map<String, Integer> matchCountsByEntryName = new HashMap<>();
Arrays.stream(tagsToFind)
.forEach(tagToFind -> {
for (String entryName : tagsByName.keySet()) {
Set<String> tags = tagsByName.get(entryName);
if (tags.contains(trimToEmpty(tagToFind))) {
matchCountsByEntryName.compute(entryName, (k, v) -> v == null ? 1 : v + 1);
}
}
});
List<Map.Entry<String, Integer>> sortedEntries = matchCountsByEntryName.entrySet().stream()
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
.collect(Collectors.toList());
Map.Entry<String, Integer> entryWithMostMatches = sortedEntries.get(0);
System.out.printf("Of the entries to be searched," +
" entry \"%s\" contains the most matches (%d).\n",
entryWithMostMatches.getKey(), entryWithMostMatches.getValue());
}
}
After many days of trying, I think I found solution to my problem. Below is the code I am using now.
public ArrayList<QuestionAnswerModal> filter(String keyword, boolean isQuestionSearch) {
ArrayList<QuestionAnswerModal> arrayList = new ArrayList<>();
HashMap<String, Integer> countList = new HashMap<>();
if (isQuestionSearch) {
for (QuestionAnswerModal modal : questionAnswerArrayList) {
if (modal.getKeyword().equalsIgnoreCase(keyword)) {
arrayList.add(modal);
}
}
return arrayList;
} else {
//will store the index of the question with largest match
int[] count = new int[questionAnswerArrayList.size()];
for (int i = 0; i < questionAnswerArrayList.size(); i++) {
List<String> keywords = new ArrayList<>();
String[] word = questionAnswerArrayList.get(i).getKeyword().split(",");
keywords = Arrays.asList(word);
String[] userKeywords = keyword.split(",");
for (int j = 0; j < userKeywords.length; j++) {
if (keywords.contains(userKeywords[j])) {
if (countList.size() == 0) {
//countList.put(userKeywords[j], 1);
count[i]++;
}
}
}
}
//index with largest match
int largest = 0;
//valu if the index
int largestCount = count[largest];
for (int i = 0; i < count.length; i++) {
if (count[i] > largestCount)
largest = i;
}
arrayList.add(questionAnswerArrayList.get(largest));
if (arrayList.size() > 0) {
lvQuestionAnswer.invalidate();
QuestionAnswerAdapter questionAnswerAdapter = new QuestionAnswerAdapter(arrayList, MainActivity.this, MainActivity.this, MainActivity.this);
lvQuestionAnswer.setAdapter(questionAnswerAdapter);
dialog.dismiss();
} else {
Toast.makeText(MainActivity.this, "No records found", Toast.LENGTH_SHORT).show();
}
return arrayList;
}
}

random elements from a list DURING the addition

There are 20 names in my code.
my function has 2 options to add elements to a list I've:
1.
Inserting all the 20 names to the list:
public void addNames() {
list.add("name1");
list.add("name2");
...
list.add("name20");
}
2.
Adding only 5 random names(from the 20 names) to the list. For doing it, I thought about 2 ways. What's the best way to random 5 names from the 20? maybe you have a better way.
A.
Using a random set of indices (each value will be between 0 to 19 because there are 20 names) and before the 'add' I'll check if adding them or not by some counter:
public void addNames() {
// adding 5 random indices between 0 to 19 to the set
Set<Integer> set = new HashSet<Integer>();
Random r = new Random();
Set<Integer> indices = new HashSet<>(numRandomNames); //==5
for (int i = 0; i < numRandomNames; ++i) {
int index = r.nextInt(numNames - 0); //==19
indices.add(index);
}
int counter = 0;
if (indices.contains(counter)) {
list.add("name1");
}
counter++;
if (indices.contains(counter)) {
list.add("name2");
}
counter++;
if (indices.contains(counter)) {
list.add("name3");
}
...
}
B.
RandomList that extends List and overrides the 'add' function to do the same as 'A.' does BUT the override 'add' will decide whether adding the value inside the function so my function will look the same as 1. with the override 'add' function
Do you think about a better solution? if not, then which one is better? (A or B?). I just saw that people recommends not to extend the java collection but I think it's the best solution from these 2 solutions.
NOTE
====
my code can have 10000 names or more even so I don't want to add all the 10,000 names to this\other list and then random 5 of them to other list. I prefer to do it DURING the addition in order to avoid many places of the list while I don't really need them.
EDIT
an answer to ProgrammerTrond:
I'm not sure I'll do it but what I asked me to show is my suggestion of 2.B:
public class RandomList<Integer> implements List<Integer> {
private int addCallsCounter;
private Set<Integer> setIndices = null;
public RandomList(final int numElements, final int maxVal, final int minVal) {
addCallsCounter = 0;
setIndices = new HashSet<Integer>(numElements);
Random r = new Random();
while (setIndices.size() < numElements) {
int index = r.nextInt(maxVal - minVal + 1) + minVal;
if (setIndices.contains(index) == false) {
setIndices.add(index);
}
}
}
#Override
public boolean add(Integer object) {
if (setIndices.contains(addCallsCounter++)) {
this.add(object);
return true;
}
return false;
}
}
and from my code I'll do so:
RandomList randList = new RandomList(5);
randList.add("name1");
randList.add("name2");
randList.add("name3");
...
randList.add("name19");
randList.add("name20");
but my problem is that I need to implement MANY abstract methods of List pfff. RandomList cann't be abstract too because then it won't be able to be instantiated.
try this:
List<Integer> index = new ArrayList<>();
List<String> five_names = new ArrsyList<>();
List<String> allnames = new ArrayList<>();
store five random values
for(int i = 0;i < 5;i++){
int index_no = getrandomNumber();
index.add(index_no);
five_names.add(allnames.get(index_no));
}
getRandomNumber method:
public int getRandomNumber(){
Random rnd = new Random();
int x = rnd.nextInt(20);
if(index.contains(x)){
return getRandomNumber();
}else{
return x
}
}
Why not like this? You don't need the random index list in your list implementation. Didn't you just want a method that would add to a list 5 random names drawn from a set of available names?
import java.util.*;
public class ListAdding {
private static List<String> allNames = Arrays.asList("name1", "name2", "name3", "name4", "name5", "name6", "name7");
public static void main(String[] args) {
new Temp().test();
}
void test() {
List<String> list = new ArrayList<>();
list.add("Bernie");
addFiveRandom(list);
for (int i = 0; i < list.size(); i++) {
System.out.println(i + ": " + list.get(i));
}
// Example: 0: Bernie
// 1: name2
// 2: name3
// 3: name6
// and so on
}
void addFiveRandom(List<String> toBeAddedTo) {
List<Integer> indices = new ArrayList<>();
while (indices.size() < 5) {
int newIndex = new Random().nextInt(5);
if (!indices.contains(newIndex))
indices.add(newIndex);
}
for (Integer index : indices) {
toBeAddedTo.add(allNames.get(index));
}
}
}

Java: Create unique List from an existing List keeping index numbers constant

I have two ArrayLists.
List of dates
List of respective data.
Both are synchronized. I sometimes have more than one data on a same date. I need to create two lists: unique dates and the data (averaged) respectively. So far, I have tried the following methods
int i = 1;
for(int it =0; it < predatetime.size() - 1; it++){
//Compare each element with the next one
if(predatetime.get(it+1) == predatetime.get(it)){
i++;
weight = preweight.get(it+1) + weight;
//If equal, add weights and increment a divisor for averaging
}
else { //if not equal, add to the new lists
if(it == predatetime.size() - 2){ //if the last element is not equal to its previous one, just add it to the list
newDateTime.add(predatetime.get(it+1));
newWeight.add(preweight.get(it+1));
break;
}
weight = weight / i;
newDateTime.add(predatetime.get(it));
newWeight.add(weight);
weight = preweight.get(it+1); //re-initialize variables
i = 1;
}
if(it == predatetime.size() - 2){
weight = weight / i;
newDateTime.add(predatetime.get(it));
newWeight.add(weight);
}
}
There are a lot of problems with this code.
If the list has only one element, it fails. (I know I can write 2 more lines to care of this). Is there a better way to do this?
I know there are similar questions on this website, but still I'm unable to resolve the problem.
This is the full solution
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
public class CustomList {
public static void main(String[] args) {
ArrayList<String> date = new ArrayList<>();
date.add("1");
date.add("2");
date.add("2");
date.add("3");
System.out.println(date);
ArrayList<Integer> value = new ArrayList<>();
value.add(1);
value.add(2);
value.add(4);
value.add(3);
System.out.println(value);
new MyCls().createList(date, value);
}
}
class MyCls {
ArrayList uniqueDate = new ArrayList<String>();
ArrayList averageValue = new ArrayList<Integer>();
LinkedHashMap store = new LinkedHashMap<String, CountEntry>();
class CountEntry {
int value;
int count;
CountEntry() {
}
CountEntry(int v, int c) {
value = v;
count = c;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
public void createList(ArrayList<String> date, ArrayList<Integer> value) {
for (int i = 0; i < date.size(); i++) {
CountEntry tmp = (CountEntry) store.get(date.get(i));
if (tmp == null) {
store.put(date.get(i), new CountEntry(value.get(i), 1));
} else {
int tmpVal = tmp.getValue();
int tmpCount = tmp.getCount();
store.put(date.get(i), new CountEntry(value.get(i) + tmpVal, ++tmpCount));
}
}
ArrayList<String> uniqueDate = new ArrayList<String>(store.keySet());
ArrayList<CountEntry> tempAvgList = new ArrayList<CountEntry>(store.values());
for (CountEntry ce : tempAvgList) {
averageValue.add(ce.getValue() / ce.getCount());
}
System.out.println("Output");
System.out.println(uniqueDate);
System.out.println(averageValue);
}
}
/*
OUTPUT Snap:
[1, 2, 2, 3]
[1, 2, 4, 3]
Output
[1, 2, 3]
[1, 3, 3]
*/
If you try to make your list elements unique why you not try to convert the list to set collection
Set<Foo> foo = new HashSet<Foo>(myList);
Why not create a Map instead with the dates as the key and have the value as a list. This will allow you to keep the dates unique, at the same allow you to have your data as a list.
Map<String, ArrayList<myData>> myMap = new HashMap<String, ArrayList<myData>>();
Then you can just find if your key exists, if it does add it to the array list by using the key to identify the correct list. If it doesnt exist it, add it to the map
Thanks to #Rambler and #JulianGurung, I created a HashMap and it works
HashMap<Integer, Float> hm = new HashMap<Integer,Float>();
int occurance = 0;
float weight = 0;
hm.put(predatetime.get(0), 0f); //initialize with the first value
for(Map.Entry m : hm.entrySet()){
for( int it = 0; it < predatetime.size(); it++){
if(m.getKey() == predatetime.get(it)){
weight = (Float) m.getValue() + preweight.get(it); //Sum all the same data in order to avg later
hm.put(predatetime.get(it), weight);
occurance++;
}
else{ //if it is not equal, add the new element to the map
hm.put(predatetime.get(it), preweight.get(it));
}
}
weight = weight / occurance;
hm.put((Integer) m.getKey(), weight);
weight = 0;
occurance = 0;
}

Categories