I've seen several other questions similiar to this one but I haven't really been able to find anything that resolves my problem.
My use case is this: user has a list of items initially (listA). They reorder the items and want to persist that order (listB), however, due to restrictions I'm unable persist the order on the backend so I have to sort listA after I retrieve it.
So basically, I have 2 ArrayLists (listA and listB). One with the specific order the lists should be in (listB) and the other has the list of items (listA). I want to sort listA based on listB.
Using Java 8:
Collections.sort(listToSort,
Comparator.comparing(item -> listWithOrder.indexOf(item)));
or better:
listToSort.sort(Comparator.comparingInt(listWithOrder::indexOf));
Collections.sort(listB, new Comparator<Item>() {
public int compare(Item left, Item right) {
return Integer.compare(listA.indexOf(left), listA.indexOf(right));
}
});
This is quite inefficient, though, and you should probably create a Map<Item, Integer> from listA to lookup the positions of the items faster.
Guava has a ready-to-use comparator for doing that: Ordering.explicit()
Let's say you have a listB list that defines the order in which you want to sort listA. This is just an example, but it demonstrates an order that is defined by a list, and not the natural order of the datatype:
List<String> listB = Arrays.asList("Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday");
Now, let's say that listA needs to be sorted according to this ordering. It's a List<Item>, and Item has a public String getWeekday() method.
Create a Map<String, Integer> that maps the values of everything in listB to something that can be sorted easily, such as the index, i.e. "Sunday" => 0, ..., "Saturday" => 6. This will provide a quick and easy lookup.
Map<String, Integer> weekdayOrder = new HashMap<String, Integer>();
for (int i = 0; i < listB.size(); i++)
{
String weekday = listB.get(i);
weekdayOrder.put(weekday, i);
}
Then you can create your custom Comparator<Item> that uses the Map to create an order:
public class ItemWeekdayComparator implements Comparator<Item>
{
private Map<String, Integer> sortOrder;
public ItemWeekdayComparator(Map<String, Integer> sortOrder)
{
this.sortOrder = sortOrder;
}
#Override
public int compare(Item i1, Item i2)
{
Integer weekdayPos1 = sortOrder.get(i1.getWeekday());
if (weekdayPos1 == null)
{
throw new IllegalArgumentException("Bad weekday encountered: " +
i1.getWeekday());
}
Integer weekdayPos2 = sortOrder.get(i2.getWeekday());
if (weekdayPos2 == null)
{
throw new IllegalArgumentException("Bad weekday encountered: " +
i2.getWeekday());
}
return weekdayPos1.compareTo(weekdayPos2);
}
}
Then you can sort listA using your custom Comparator.
Collections.sort(listA, new ItemWeekdayComparator(weekdayOrder));
Speed improvement on JB Nizet's answer (from the suggestion he made himself). With this method:
Sorting a 1000 items list 100 times improves speed 10 times on my
unit tests.
Sorting a 10000 items list 100 times improves speed 140 times (265 ms for the whole batch instead of 37 seconds) on my
unit tests.
This method will also work when both lists are not identical:
/**
* Sorts list objectsToOrder based on the order of orderedObjects.
*
* Make sure these objects have good equals() and hashCode() methods or
* that they reference the same objects.
*/
public static void sortList(List<?> objectsToOrder, List<?> orderedObjects) {
HashMap<Object, Integer> indexMap = new HashMap<>();
int index = 0;
for (Object object : orderedObjects) {
indexMap.put(object, index);
index++;
}
Collections.sort(objectsToOrder, new Comparator<Object>() {
public int compare(Object left, Object right) {
Integer leftIndex = indexMap.get(left);
Integer rightIndex = indexMap.get(right);
if (leftIndex == null) {
return -1;
}
if (rightIndex == null) {
return 1;
}
return Integer.compare(leftIndex, rightIndex);
}
});
}
Problem : sorting a list of Pojo on the basis of one of the field's all possible values present in another list.
Take a look at this solution, may be this is what you are trying to achieve:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<Employee> listToSort = new ArrayList<>();
listToSort.add(new Employee("a", "age11"));
listToSort.add(new Employee("c", "age33"));
listToSort.add(new Employee("b", "age22"));
listToSort.add(new Employee("a", "age111"));
listToSort.add(new Employee("c", "age3"));
listToSort.add(new Employee("b", "age2"));
listToSort.add(new Employee("a", "age1"));
List<String> listWithOrder = new ArrayList<>();
listWithOrder.add("a");
listWithOrder.add("b");
listWithOrder.add("c");
Collections.sort(listToSort, Comparator.comparing(item ->
listWithOrder.indexOf(item.getName())));
System.out.println(listToSort);
}
}
class Employee {
String name;
String age;
public Employee(String name, String age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public String getAge() {
return age;
}
#Override
public String toString() {
return "[name=" + name + ", age=" + age + "]";
}
}
O U T P U T
[[name=a, age=age11], [name=a, age=age111], [name=a, age=age1], [name=b, age=age22], [name=b, age=age2], [name=c, age=age33], [name=c, age=age3]]
Here is a solution that increases the time complexity by 2n, but accomplishes what you want. It also doesn't care if the List R you want to sort contains Comparable elements so long as the other List L you use to sort them by is uniformly Comparable.
public class HeavyPair<L extends Comparable<L>, R> implements Comparable<HeavyPair<L, ?>> {
public final L left;
public final R right;
public HeavyPair(L left, R right) {
this.left = left;
this.right = right;
}
public compareTo(HeavyPair<L, ?> o) {
return this.left.compareTo(o.left);
}
public static <L extends Comparable<L>, R> List<R> sort(List<L> weights, List<R> toSort) {
assert(weights.size() == toSort.size());
List<R> output = new ArrayList<>(toSort.size());
List<HeavyPair<L, R>> workHorse = new ArrayList<>(toSort.size());
for(int i = 0; i < toSort.size(); i++) {
workHorse.add(new HeavyPair(weights.get(i), toSort.get(i)))
}
Collections.sort(workHorse);
for(int i = 0; i < workHorse.size(); i++) {
output.add(workHorse.get(i).right);
}
return output;
}
}
Excuse any terrible practices I used while writing this code, though. I was in a rush.
Just call HeavyPair.sort(listB, listA);
Edit: Fixed this line return this.left.compareTo(o.left);. Now it actually works.
Here is an example of how to sort a list and then make the changes in another list according to the changes exactly made to first array list. This trick will never fails and ensures the mapping between the items in list. The size of both list must be same to use this trick.
ArrayList<String> listA = new ArrayList<String>();
ArrayList<String> listB = new ArrayList<String>();
int j = 0;
// list of returns of the compare method which will be used to manipulate
// the another comparator according to the sorting of previous listA
ArrayList<Integer> sortingMethodReturns = new ArrayList<Integer>();
public void addItemstoLists() {
listA.add("Value of Z");
listA.add("Value of C");
listA.add("Value of F");
listA.add("Value of A");
listA.add("Value of Y");
listB.add("this is the value of Z");
listB.add("this is the value off C");
listB.add("this is the value off F");
listB.add("this is the value off A");
listB.add("this is the value off Y");
Collections.sort(listA, new Comparator<String>() {
#Override
public int compare(String lhs, String rhs) {
// TODO Auto-generated method stub
int returning = lhs.compareTo(rhs);
sortingMethodReturns.add(returning);
return returning;
}
});
// now sort the list B according to the changes made with the order of
// items in listA
Collections.sort(listB, new Comparator<String>() {
#Override
public int compare(String lhs, String rhs) {
// TODO Auto-generated method stub
// comparator method will sort the second list also according to
// the changes made with list a
int returning = sortingMethodReturns.get(j);
j++;
return returning;
}
});
}
try this for java 8:
listB.sort((left, right) -> Integer.compare(list.indexOf(left), list.indexOf(right)));
or
listB.sort(Comparator.comparingInt(item -> list.indexOf(item)));
import java.util.Comparator;
import java.util.List;
public class ListComparator implements Comparator<String> {
private final List<String> orderedList;
private boolean appendFirst;
public ListComparator(List<String> orderedList, boolean appendFirst) {
this.orderedList = orderedList;
this.appendFirst = appendFirst;
}
#Override
public int compare(String o1, String o2) {
if (orderedList.contains(o1) && orderedList.contains(o2))
return orderedList.indexOf(o1) - orderedList.indexOf(o2);
else if (orderedList.contains(o1))
return (appendFirst) ? 1 : -1;
else if (orderedList.contains(o2))
return (appendFirst) ? -1 : 1;
return 0;
}
}
You can use this generic comparator to sort list based on the the other list.
For example, when appendFirst is false below will be the output.
Ordered list: [a, b]
Un-ordered List: [d, a, b, c, e]
Output:
[a, b, d, c, e]
One way of doing this is looping through listB and adding the items to a temporary list if listA contains them:
List<?> tempList = new ArrayList<?>();
for(Object o : listB) {
if(listA.contains(o)) {
tempList.add(o);
}
}
listA.removeAll(listB);
tempList.addAll(listA);
return tempList;
Not completely clear what you want, but if this is the situation:
A:[c,b,a]
B:[2,1,0]
And you want to load them both and then produce:
C:[a,b,c]
Then maybe this?
List c = new ArrayList(b.size());
for(int i=0;i<b.size();i++) {
c.set(b.get(i),a.get(i));
}
that requires an extra copy, but I think to to it in place is a lot less efficient, and all kinds of not clear:
for(int i=0;i<b.size();i++){
int from = b.get(i);
if(from == i) continue;
T tmp = a.get(i);
a.set(i,a.get(from));
a.set(from,tmp);
b.set(b.lastIndexOf(i),from);
}
Note I didn't test either, maybe got a sign flipped.
Another solution that may work depending on your setting is not storing instances in listB but instead indices from listA. This could be done by wrapping listA inside a custom sorted list like so:
public static class SortedDependingList<E> extends AbstractList<E> implements List<E>{
private final List<E> dependingList;
private final List<Integer> indices;
public SortedDependingList(List<E> dependingList) {
super();
this.dependingList = dependingList;
indices = new ArrayList<>();
}
#Override
public boolean add(E e) {
int index = dependingList.indexOf(e);
if (index != -1) {
return addSorted(index);
}
return false;
}
/**
* Adds to this list the element of the depending list at the given
* original index.
* #param index The index of the element to add.
*
*/
public boolean addByIndex(int index){
if (index < 0 || index >= this.dependingList.size()) {
throw new IllegalArgumentException();
}
return addSorted(index);
}
/**
* Returns true if this list contains the element at the
* index of the depending list.
*/
public boolean containsIndex(int index){
int i = Collections.binarySearch(indices, index);
return i >= 0;
}
private boolean addSorted(int index){
int insertIndex = Collections.binarySearch(indices, index);
if (insertIndex < 0){
insertIndex = -insertIndex-1;
this.indices.add(insertIndex, index);
return true;
}
return false;
}
#Override
public E get(int index) {
return dependingList.get(indices.get(index));
}
#Override
public int size() {
return indices.size();
}
}
Then you can use this custom list as follows:
public static void main(String[] args) {
class SomeClass{
int index;
public SomeClass(int index) {
super();
this.index = index;
}
#Override
public String toString() {
return ""+index;
}
}
List<SomeClass> listA = new ArrayList<>();
for (int i = 0; i < 100; i++) {
listA.add(new SomeClass(i));
}
SortedDependingList<SomeClass> listB = new SortedDependingList<>(listA);
Random rand = new Random();
// add elements by index:
for (int i = 0; i < 5; i++) {
int index = rand.nextInt(listA.size());
listB.addByIndex(index);
}
System.out.println(listB);
// add elements by identity:
for (int i = 0; i < 5; i++) {
int index = rand.nextInt(listA.size());
SomeClass o = listA.get(index);
listB.add(o);
}
System.out.println(listB);
}
Of course, this custom list will only be valid as long as the elements in the original list do not change. If changes are possible, you would need to somehow listen for changes to the original list and update the indices inside the custom list.
Note also, that the SortedDependingList does currently not allow to add an element from listA a second time - in this respect it actually works like a set of elements from listA because this is usually what you want in such a setting.
The preferred way to add something to SortedDependingList is by already knowing the index of an element and adding it by calling sortedList.addByIndex(index);
If the two lists are guaranteed to contain the same elements, just in a different order, you can use List<T> listA = new ArrayList<>(listB) and this will be O(n) time complexity. Otherwise, I see a lot of answers here using Collections.sort(), however there is an alternative method which is guaranteed O(2n) runtime, which should theoretically be faster than sort's worst time complexity of O(nlog(n)), at the cost of 2n storage
Set<T> validItems = new HashSet<>(listB);
listA.clear();
listB.forEach(item -> {
if(validItems.contains(item)) {
listA.add(item);
}
});
List<String> listA;
Comparator<B> comparator = Comparator.comparing(e -> listA.indexOf(e.getValue()));
//call your comparator inside your list to be sorted
listB.stream().sorted(comparator)..
Like Tim Herold wrote, if the object references should be the same, you can just copy listB to listA, either:
listA = new ArrayList(listB);
Or this if you don't want to change the List that listA refers to:
listA.clear();
listA.addAll(listB);
If the references are not the same but there is some equivalence relationship between objects in listA and listB, you could sort listA using a custom Comparator that finds the object in listB and uses its index in listB as the sort key. The naive implementation that brute force searches listB would not be the best performance-wise, but would be functionally sufficient.
IMO, you need to persist something else. May be not the full listB, but something. May be just the indexes of the items that the user changed.
Try this. The code below is general purpose for a scenario where listA is a list of Objects since you did not indicate a particular type.
Object[] orderedArray = new Object[listA.size()];
for(int index = 0; index < listB.size(); index ++){
int position = listB.get(index); //this may have to be cast as an int
orderedArray[position] = listA.get(index);
}
//if you receive UnsupportedOperationException when running listA.clear()
//you should replace the line with listA = new List<Object>()
//using your actual implementation of the List interface
listA.clear();
listA.addAll(orderedArray);
Just encountered the same problem.
I have a list of ordered keys, and I need to order the objects in a list according to the order of the keys.
My lists are long enough to make the solutions with time complexity of N^2 unusable.
My solution:
<K, T> List<T> sortByOrder(List<K> orderedKeys, List<T> objectsToOrder, Function<T, K> keyExtractor) {
AtomicInteger ind = new AtomicInteger(0);
Map<K, Integer> keyToIndex = orderedKeys.stream().collect(Collectors.toMap(k -> k, k -> ind.getAndIncrement(), (oldK, newK) -> oldK));
SortedMap<Integer, T> indexToObj = new TreeMap<>();
objectsToOrder.forEach(obj -> indexToObj.put(keyToIndex.get(keyExtractor.apply(obj)), obj));
return new ArrayList<>(indexToObj.values());
}
The time complexity is O(N * Log(N)).
The solution assumes that all the objects in the list to sort have distinct keys. If not then just replace SortedMap<Integer, T> indexToObj by SortedMap<Integer, List<T>> indexToObjList.
To avoid having a very inefficient look up, you should index the items in listB and then sort listA based on it.
Map<Item, Integer> index = IntStream.range(0, listB.size()).boxed()
.collect(Collectors.toMap(listB::get, x -> x));
listA.sort((e1, e2) -> Integer.compare(index.get(c1), index.get(c2));
So for me the requirement was to sort originalList with orderedList. originalList always contains all element from orderedList, but not vice versa. No new elements.
fun <T> List<T>.sort(orderedList: List<T>): List<T> {
return if (size == orderedList.size) {
orderedList
} else {
var keepIndexCount = 0
mapIndexed { index, item ->
if (orderedList.contains(item)) {
orderedList[index - keepIndexCount]
} else {
keepIndexCount++
item
}
}
}}
P.S. my case was that I have list that user can sort by drag and drop, but some items might be filtered out, so we preserve hidden items position.
If you want to do it manually. Solution based on bubble sort (same length required):
public void sortAbasedOnB(String[] listA, double[] listB) {
for (int i = 0; i < listB.length - 1; i++) {
for (int j = listB.length - 1; j > i; j--) {
if (listB[j] < listB[j - 1]){
double tempD = listB[j - 1];
listB[j - 1] = listB[j];
listB[j] = tempD;
String tempS = listA[j - 1];
listA[j - 1] = listA[j];
listA[j] = tempS;
}
}
}
}
If the object references should be the same, you can initialize listA new.
listA = new ArrayList(listB)
In Java there are set of classes which can be useful to sort lists or arrays. Most of the following examples will use lists but the same concept can be applied for arrays. A example will show this.
We can use this by creating a list of Integers and sort these using the Collections.sort(). The Collections (Java Doc) class (part of the Java Collection Framework) provides a list of static methods which we can use when working with collections such as list, set and the like. So in a nutshell, we can sort a list by simply calling: java.util.Collections.sort(the list) as shown in the following example:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class example {
public static void main(String[] args) {
List<Integer> ints = new ArrayList<Integer>();
ints.add(4);
ints.add(3);
ints.add(7);
ints.add(5);
Collections.sort(ints);
System.out.println(ints);
}
}
The above class creates a list of four integers and, using the collection sort method, sorts this list (in one line of code) without us having to worry about the sorting algorithm.
I have got an array list of animals, on click on 'select' button I
would like to randomly select these animals and pass animals into two
arrays (split) called 'teamA and teamB'. Here is my code, but I am getting the same array list always as per screenhot link ? Could someone please
help me to figure out the problem ?
import java.lang.Math;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class RandomExample {
private Random random = new Random();
public static void main(String[] args) {
// 'list' array list contains animals
List<String> list = new ArrayList<String>();
list.add("Tiger");
list.add("Crocodile");
list.add("Cat");
list.add("Dog");
list.add("Elephant");
list.add("Lion");
list.add("Deer");
list.add("Eagle");
RandomExample obj = new RandomExample();
for(int i = 0; i < 10; i++){
obj.getRandomList(list);
List<String> teamA = list.subList(0, 4);
List<String> teamB = list.subList(4, 8);
System.out.println(teamA);
System.out.println(teamB);
}
}
public String getRandomList(List<String> list) {
//0-4
int index = random.nextInt(list.size());
System.out.println("\nIndex :" + index );
return list.get(index);
}
}
As I asked I am not sure why you do not capture the returned string from getRandomList()… I am guessing you are thinking that the list gets returned? Another issue is that when you get a random number from the list you could get the same number. Therefore you will possibly get the same animal on both teams or even the same animal twice or more on the same team. When you put an animal on a team… you need to remove them from the list.
Below I create the two teams. Then setup two loops, one for each team. Using your getRandomList method to get a random animal then remove that animal from the list. After we have both lists, print the results. Hope this helps.
Edit: As per OP request to have a different number of animals for the teams.
Example: use 5 total animals for the teams.
Obviously it’s better to look at the amount of available data before you actually set the team sizes. Example: if you want teamB to have 5 and teamA to have 4, then there better be at least 9 animals in the list. So check the team sizes before you start the loops. If totalAnimalsForTeams is greater than the number of animals in the list or totalAnimalsForTeams is less than two , then we need to indicate this to the user and exit. This approach allows you to use only part of the list if needed. In the implementation below, if the totalAnimalsForTeams is an odd number the second loop will have the extra animal member.
public class Main
{
private static Random random = new Random();
public static void main(String[] args)
{
// 'list' array list contains animals
List<String> list = new ArrayList<String>();
list.add("Tiger");
list.add("Crocodile");
list.add("Cat");
list.add("Dog");
list.add("Elephant");
list.add("Lion");
list.add("Deer");
list.add("Eagle");
list.add("Monster");
list.add("Alien");
list.add("Vombie");
list.add("Politician");
list.add("Donkeye");
List<String> teamA = new ArrayList<String>();
List<String> teamB = new ArrayList<String>();
String newAnimal;
int totalAnimalsForTeams = 7; // <- probably get this value from the user?
if (totalAnimalsForTeams > list.size())
{
System.out.println("There are only " + list.size() + " animals in the list. Requested animals was: " + totalAnimalsForTeams);
return;
}
int firstHalf = totalAnimalsForTeams / 2;
if (firstHalf < 1)
{
System.out.println("Requested " + totalAnimalsForTeams + " animals for teams... not enough to make two teams!");
return;
}
for(int i = 0; i < firstHalf; i++)
{
newAnimal = getRandomList(list);
teamA.add(newAnimal);
list.remove(newAnimal);
}
int secondHalf = totalAnimalsForTeams - firstHalf;
for(int i = 0; i < secondHalf; i++)
{
newAnimal = getRandomList(list);
teamB.add(newAnimal);
list.remove(newAnimal);
}
System.out.println(teamA);
System.out.println(teamB);
}
public static String getRandomList(List<String> list) {
//0-4
if (list.size() > 1)
{
int index = random.nextInt(list.size());
//System.out.println("\nIndex :" + index );
return list.get(index);
}
else
{
return list.get(0);
}
}
}
try this .. it should work ..
import java.lang.Math;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Collections;
import java.util.Random;
public class RandomExample {
private Random random = new Random();
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("Tiger");
list.add("Crocodile");
list.add("Cat");
list.add("Dog");
list.add("Elephant");
list.add("Lion");
list.add("Deer");
list.add("Eagle");
RandomExample obj = new RandomExample();
List<String> teamA = list.subList(0,4);
List<String> teamB = list.subList(4,8);
for(int i = 0; i < 10; i++){
obj.getRandomList(list);
teamA = list.subList(0,4);
teamB = list.subList(4,8);
Collections.rotate(teamA, 1);
Collections.rotate(teamB, 1);
System.out.println(teamA);
System.out.println(teamB);
}
}
public String getRandomList(List<String> list) {
//0-4
int index = random.nextInt(list.size());
System.out.println("\nIndex :" + index );
return list.get(index);
}
}
Calling obj.getRandomList(list); returns a String but you donot do anything with it. I know this method is supposed to return a randomly picked item.
To correctly do what you are asking for, You would have to declare a new ArrayList say temp and then assign list to it.
To avoid Repetition of the same object, you need to remove the randomly generated item from the temp ArrayList created.
Then after you are done, remove the temp ArrayList created.
I have made the necessary changes to your main method as per the suggestions I made.
public static void main(String[] args) {
// 'list' array list contains animals
List<String> list = new ArrayList<String>();
list.add("Tiger");
list.add("Crocodile");
list.add("Cat");
list.add("Dog");
list.add("Elephant");
list.add("Lion");
list.add("Deer");
list.add("Eagle");
RandomExample obj = new RandomExample();
List<String> teamA = new ArrayList<String>();
List<String> teamB = new ArrayList<String>();
List<String> temp = list;
String animal;
for(int i = 0; i < 10; i++){
if(temp.isEmpty() == false){
animal = obj.getRandomList(temp);
if(i <= 3){
teamA.add(animal);
}else{
teamB.add(animal);
}
temp.remove(animal);
//System.out.println("temp is "+temp);
}
}
temp.clear();
System.out.println(teamA);
System.out.println(teamB);
}
I'm trying to create a method which will search through an ArrayList containing several words searching for a specific String. Where if the String is not equal to any words in the ArrayList it will add the word to the list, and if the word already exists in the list, it will count how many times the word occurs and then add one more, which will represent the last String input.
This is what I've got so far in my code:
public void leggTilOrd(String ord) {
if (Ord.contains(ord)) {
teller++;
}
if (!Ord.contains(ord)) {
Ord.add(ord);
}
System.out.println(teller);
}
Obviously this will only add one more number in the counter (teller), so what I'm trying to achieve it to add 1 on top of all the occurrences of that specific String in the list and this is where I'm stuck.
Edit: I should also mention that Ord is an ArrayList I've created earlier in the code.
Here's the full code:
import java.util.Scanner;
import java.io.File;
import java.util.ArrayList;
public class Ordliste {
private ArrayList<String> Ord = new ArrayList<String>();
private int teller = 0;
public void lesBok (String filnavn) throws Exception{
Scanner fil = new Scanner(new File(filnavn));
while(fil.hasNextLine()){
Ord.add(fil.next());
} fil.close();
}
public void leggTilOrd(String ord){
if(Ord.contains(ord)){
teller++;
} if (!Ord.contains(ord)){
Ord.add(ord);
} System.out.println(teller);
}
}
Instead of doing this , you can keep track of the strings in your list using a Set
Set<String> set = new HashSet<String>();
public void leggTilOrd(String ord) {
if (set.contains(ord) != null) {
teller++;
} else {
Ord.add(ord);
set.add(ord);
}
System.out.println(teller);
}
Are you tied to using an ArrayList? I'd recommend using a Map<String, Integer> instead and store the number of occurrences of a specific string as value in the map:
if (map.get(ord) != null) {
map.put(ord, map.get(ord) + 1);
}
else {
map.put(ord, 1);
}
Still not sure exactly what you are trying to accomplish, do you increment teller each time you add a different word?
This is a simple way to do what I believe you are attempting, and should get you going in the right direction.
import java.util.Arrays;
import java.util.List;
public class test
{
static int teller = 0;
static List<String> words = Arrays.asList("dog", "cat", "dog");
public static void main(String[] args)
{
System.out.println(teller);
addAndCount("dog");
System.out.println(teller);
teller = 0;
addAndCount("cat");
System.out.println(teller);
teller = 0;
addAndCount("fish");
System.out.println(teller);
}
public static void addAndCount(String newWord)
{
teller += 1 + Math.toIntExact(
words.stream().filter(string -> newWord.equals(string)).count());
words.add(newWord);
}
}
I've been tasked with sorting a list of categories. A shopping list, if you will. These categories are inputs from the user, items to be bought. After the string name of the category in question is input (using scanner of course) the user is then able to put in the quantity of that category (an integer), followed by the unit cost of that category(a double).
They are prompted to repeat this until the category they name is "end".
This is all fine and dandy, and I've already written code that takes all of this information and finds and prints out the largest cost item, the largest quantity item, and other info. What I need help with is my duplicate categories. For example, suppose the user puts in "cars" followed by an integer 3, followed by the number 24000.00. They then put in "refigerators" followed by 1, followed by 1300.00. And then the user puts in a duplicate of the first entry, which is "cars" followed by an integer 5, followed by the double 37000.00. How can I get my code revisit older entries, add the new quantity to the old one, and store that value without overriding the old stuff? I also need to find the largest average cost of the items in the list. I am new to HashMap, so I'm struggling with the code: // create an arrayList to store values
// create an arrayList to store values
ArrayList<String> listOne = new ArrayList<String>();
listOne.add("+ 1 item");
listOne.add("+ 1 item");
listOne.add("+ 1 item");
// create list two and store values
ArrayList<String> listTwo = new ArrayList<String>();
listTwo.add("+1 item");
listTwo.add("+1 item");
// put values into map
multiMap.put("some sort of user input detailing the name of the item", listOne);
multiMap.put("some other input detailing the name of the next item", listTwo);
// i need the user to input items until they type "end"
// Get a set of the entries
Set<Entry<String, ArrayList<String>>> setMap = multiMap.entrySet();
// time for an iterator
Iterator<Entry<String, ArrayList<String>>> iteratorMap = setMap.iterator();
System.out.println("\nHashMap with Multiple Values");
// display all the elements
while(iteratorMap.hasNext()) {
Map.Entry<String, ArrayList<String>> entry =
(Map.Entry<String, ArrayList<String>>) iteratorMap.next();
String key = entry.getKey();
List<String> values = entry.getValue();
System.out.println("Key = '" + key + "' has values: " + values);
}
// all that up there gives me this:
HashMap with Multiple Values
Key = 'some other input detailing the name of the next item' has values: [+1 item, +1 item]
Key = 'some sort of user input detailing the name of the item' has values: [+ 1 item, + 1 item, + 1 item]
but I haven't given the user a chance to input the number of items or the cost.... I'm lost.
Try this small bit of sample code, it includes a Main class and two dependency classes, StatsPrinter and ShoppingEntry
package com.company;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Main {
public static void main(String[] args) throws IOException {
String category;
String quantity;
String value;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
HashMap<String, List<ShoppingEntry>> shoppingList = new HashMap<String, List<ShoppingEntry>>();
while(true) {
System.out.print("Enter the category of your item: ");
category = bufferedReader.readLine();
if("end".equals(category)){
break;
}
System.out.print("Enter the quantity of your item: ");
quantity = bufferedReader.readLine();
System.out.print("Enter the value of your item: ");
value = bufferedReader.readLine();
if (shoppingList.containsKey(category)) {
shoppingList.get(category).add(new ShoppingEntry(Integer.parseInt(quantity), Double.parseDouble(value)));
}else{
shoppingList.put(category, new ArrayList<ShoppingEntry>());
shoppingList.get(category).add(new ShoppingEntry(Integer.parseInt(quantity), Double.parseDouble(value)));
}
}
StatsPrinter.printStatistics(shoppingList);
}
}
and the ShoppingEntry class
package com.company;
public class ShoppingEntry {
private int quantity;
private double price;
public ShoppingEntry(){
quantity = 0;
price = 0;
}
public ShoppingEntry(int quantity, double price){
this.quantity = quantity;
this.price = price;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
and finally the StatsPrinter class, which leverages the data structure of the HashMap of ShoppingEntry's to print the desired statistics
package com.company;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.List;
public class StatsPrinter {
private static DecimalFormat format = new DecimalFormat("#.##");
public static void printStatistics(HashMap<String, List<ShoppingEntry>> shoppingList) {
printNuumberOfItems(shoppingList);
printLargestValue(shoppingList);
printLargestAverage(shoppingList);
}
private static void printNuumberOfItems(HashMap<String, List<ShoppingEntry>> shoppingList) {
System.out.println("There are " + shoppingList.keySet().size() + " items in your Shopping List");
}
private static void printLargestValue(HashMap<String, List<ShoppingEntry>> shoppingList) {
double currentLargestPrice = 0;
String largestPriceCategory = new String();
for(String keyValue : shoppingList.keySet()) {
for(ShoppingEntry entry : shoppingList.get(keyValue)) {
if (entry.getPrice() > currentLargestPrice) {
currentLargestPrice = entry.getPrice();
largestPriceCategory = keyValue;
}
}
}
System.out.println(largestPriceCategory + " has the largest value of: " + format.format(currentLargestPrice));
}
private static void printLargestAverage(HashMap<String, List<ShoppingEntry>> shoppingList) {
double currentLargestAverage = 0;
String largestAverageCategory = new String();
double totalCost = 0;
int numberOfItems = 0;
for(String keyValue : shoppingList.keySet()) {
for(ShoppingEntry entry : shoppingList.get(keyValue)) {
totalCost += entry.getPrice();
numberOfItems += entry.getQuantity();
}
if((totalCost / numberOfItems) > currentLargestAverage) {
currentLargestAverage = totalCost / numberOfItems;
largestAverageCategory = keyValue;
}
}
System.out.println(largestAverageCategory + " has the largest average value of: " + format.format(currentLargestAverage));
}
}
Instead of manipulating three separate values, create a class called Item. Implement the Comparable interface such that two Items are equal if they share a common name. See the Javadoc for Comparable for instructions on defining the interface.
public class Item implements Comparable {
[...]
}
Create a list of items.
List<Item> shoppingList;
When you want to add an item to the list, check if the list already contains it first.
// If it's already in the list, add their quantities together
if (shoppingList.contains(newItem))
shoppingList.get(newItem).quantity += newItem.quantity
// Otherwise, add it to the list
else
shoppingList.add(newItem);
I'd like to create a list of tuples from each value within a set of lists. The set of lists can be open, but for the example I have the following three lists of Strings.
L1: (one, two three)
L2: (a, b, c)
L3: (yes, no)
I would like to return a list of tuples, where in each tuple I have on element from each list. In this case, I will have 18 combinations (3 x 3 x 2)
T1: (one, a, yes)
T2: (one, a, no)
T3: (one, b, yes)
T4: (one, b, no)
T5: (one, c, yes)
T6: (one, c, no)
T7: (two, a, yes)
and so on. In this case we're using Java.
List<List<String>> list = getInput();
List<List<String> tuples = combinations(list);
where getInput() returns my input (L1, L2, L3), and combinations creates my output (T1, T2, T3...)
Since #Ted Hopp posted a recursive solution i am posting a non-recursive solution.
This is a working solution,
/**
* #author kalyan
*
*/
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
public class Permutation
{
public static void printLists(List<List<String>> list) {
for(List<String> lstItem : list) {
System.out.println(lstItem.toString());
}
}
public static List<List<String>> recurse(List<LinkedList<String>> list ) {
List<List<String>> out = new ArrayList<List<String>>();
Stack<String> mystack = new Stack<String>();
int i = 0;
while (! (i == 0 && list.get(0).get(0).equals("TAIL"))) {
if ( i >= list.size()) {
/* We have got one element from all the list */
out.add(new ArrayList<String>(mystack));
/* Go back one row */
i --;
/* remove the last added element */
mystack.pop();
continue;
}
LinkedList<String> tuple = list.get(i);
if (tuple.getFirst().equals("TAIL")) {
/* We have finished one sub list go back one row */
i--;
mystack.pop();
/* also fall below to move the TAIL from begining to end */
}
else {
mystack.add(tuple.getFirst());
i++;
}
tuple.add(tuple.getFirst());
tuple.removeFirst();
}
return out;
}
public static void main(String[] args) {
List<LinkedList<String>> list = new ArrayList<LinkedList<String>>();
List<List<String>> perm = new ArrayList<List<String>>();
/* keep TAIL, so that we know processed a list */
LinkedList<String> num = new LinkedList<String>();
num.add("one"); num.add("two"); num.add("three"); num.add("TAIL");
LinkedList<String> alpha = new LinkedList<String>();
alpha.add("a"); alpha.add("b"); alpha.add("c"); alpha.add("TAIL");
LinkedList<String> bool = new LinkedList<String>();
bool.add("yes"); bool.add("no"); bool.add("tristate"); bool.add("TAIL");
list.add(num); list.add(alpha); list.add(bool);
perm = recurse (list);
printLists(perm);
}
}
This should be pretty easy with a recursive function. This is untested:
List<List<String>> listOfTuples(<List<List<String>> list) {
ArrayList<List<String>> result = new ArrayList<List<String>>();
List<String> prefix = new ArrayList<String>();
recurse(0, list, prefix, result);
return result;
}
void recurse(int index,
List<List<String>> input,
List<String> prefix,
List<List<String>> output)
{
if (index >= input.size()) {
output.add(new ArrayList<String>(prefix));
} else {
List<String> next = input.get(index++);
for (String item : next) {
prefix.add(item);
recurse(index, input, prefix, output);
prefix.remove(item);
}
}
}
The recursive solution above fails if any of the lists shares common elements.
Using a Stack and pushing/popping elements rather than adding/removing them from a list fixes it.
Here's the revised code for the recurse method:
void recurse(int index, List<List<String>> input, Stack<String> prefix, List<List<String>> output) {
if (index >= input.size()) {
String[] tuple = new String[prefix.size()];
prefix.copyInto(tuple);
output.add(Arrays.asList(tuple));
} else {
List<String> next = input.get(index++);
for (String item : next) {
prefix.push(item);
recurse(index, input, prefix, output);
prefix.pop();
}
}
}
1. Impose an arbitrary ordering on the lists.
For instance, create a list with order
given by index.
2. Call permute(thelists[1..n], buffer[1..n], 1).
permute(thelist[1..n], buffer[1..n], pos)
1. if pos > n then print buffer
2. else then
3. for i = 1 to |thelists[pos]| do
4. buffer[pos] = thelists[pos][i]
5. permute(thelists[1..n], buffer[1..n], pos + 1)