I am having difficulty mapping the data in two lists into a third list. My sample data is as follows:
Categorylist ID: 1,2,3,4,5,6,7,8,9,10,42,46,49,50
CurrentMonthByCat ID: 1,2,3,4,5,6,7,8,9,10,42,49,50
(the transaction amount value for CurrentMonthByCat: 92,46,40,180,60,10,1510,200,500,10,234,12)
There is a missing 46 in the currentMonthByCat. I am trying to do it in a way such that if the currentMonthByCat ID does not exist from the categorylist ID, I will insert 0 into the third list rather than getting the transaction amount from CurrentMonthByCat and push it into the third list.
ArrayList<Double> total = new ArrayList<Double>();
for(int i = 0; i < categorylist.size(); i++){
for(int j = 0; j < currentMonthByCat.size(); j++){
if(categorylist.get(i).getCategoryID().equals(currentMonthByCat.get(j).getCategory().getCategoryID())){
Log.d("IIIII", categorylist.get(i).getCategoryID());
Log.d("JJJJJ", currentMonthByCat.get(j).getCategory().getCategoryID());
total.add((double)currentMonthByCat.get(j).getTransactionAmt());
}else{
total.add(0.0);
}
}
}
for(int k = 0; k < total.size(); k++){
Log.d("KKKKK", String.valueOf(total.get(k)));
}
But the printed out result of total list is:
92,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,0,0...
What I expected is:
92,46,40,180,60,10,1510,200,500,10,0,234,12
I wanted to insert 0 only if the ID in currentMonthByCat does not match the ID in categorylist. For instance, the ID 46 which is the 3rd position from the right.
I realized the reason is because firstly I inserted 92 into third array, then the categorylist ID is still at 1, then it will compare with all the rest in the currentMonthByCat before moving to ID 2. That is why the unnecessary zeros. But I not sure how to actually sort it to achieve what I wanted.
Any ideas?
Thanks in advance.
It's easy one. you can't take a decision of adding zero or value in the total array unless the inner loop finish. so probably you add element existAtIndex and initialize it with -1 and in the loop if you find the element then assign the index to existAtIndex and break the loop or if it's not exist then you add zero. so the code will be something like :
ArrayList<Double> total = new ArrayList<Double>();
int existAtIndex;
for(int i = 0; i < categorylist.size(); i++){
// search for the element index
existAtIndex = -1;
for(int j = 0; j < currentMonthByCat.size(); j++){
if(categorylist.get(i).getCategoryID().equals(currentMonthByCat.get(j).getCategory().getCategoryID())){
existAtIndex = j;
break;
}
}
// add the value in the element index or add zero if the element not exist
if (existAtIndex != -1) {
total.add((double)currentMonthByCat.get(existAtIndex).getTransactionAmt());
}
else {
total.add(0.0);
}
}
for(int k = 0; k < total.size(); k++){
Log.d(String.valueOf(total.get(k)));
}
For better code you could use contains method to check for the item if exist or not in arrayList instead of using the basic loop. Good luck
You have a lot of code for what you are trying to do here. I think the following snippet does what you want in a very readable and maintainable way.
//First of all we are interested in getting a transaction amount for each value in currentMonthByCat
//so loop around using your object (not sure what it's called)
for(CurrentMonth value : currentMonthByCat){
//check if it's present.
//We create a new method here that gets you your category list as a list of integers.
//This is key to making the whole method much more readable.
if(categorylist.getIdsAsList().contains(value.getCategory().getCategoryID())){
//it is so add it
total.add(value.getTransactionAmt());
} else {
//it's not so add a 0
total.add(0.0);
}
}
The getIdsAsList method could look like this:
public List<Integer> getIdsAsList(){
List<Integer> result = new ArrayList<>();
for (CategoryListItem item : categorylist) {
result.add(item.getCategoryId());
}
return result;
}
Put your values into Map<Integer, Double>
Map<Integer, Double> map = new HashMap<Integer, Double>();
for (int i = 0; i < currentMonthByCat.size(); ++i) {
//... categoryId = currentMonthByCat.get(i).categoryId
//... amount = currentMonthByCat.get(i).amount
map.put(categoryId, amount);
}
Then traverse the map using values from Categorylist ID:
// create result arraylist
ArrayList<Double> total = new ArrayList<Double>();
for (int i = 0; i < categorylist.size(); ++i) {
Double amount = map.get(categorylist.get(i));
if (amount == null) {
total.add(0.0);
} else {
total.add(amount);
}
}
The result list total will contain amounts for existing mappings, or zeros for non-existent.
Other way
If it is guaranteed categorylist is sorted and CurrentMonthByCat is sorted
you can then traverse one of the lists while keeping index/cursor to the other list and not iterating the other list from the beginning, but from previously remembered cursor value, resulting in better average performance than n^2
Related
Currently working on a method that takes a n*n matrix as input and returns an array consisting of all elements that are found in each sub-array. However, since I need it to also include duplicates etc, it's harder than I thought.
Googled the hell out of it, however, yet to find a solution which matches my criteria of repetition.
Currently I have this, which compares the element's of the first row with every other row and all their elements. If the counter gets to the length where it confirms that the element indeed is present in all rows, it adds it to the array. However, this has faults in it. First of all, since I create a set array in the beginning with the maximum possible length, it might return an array with non-needed 0's in it. And second, the duplicate part is not working correctly, struggling to implement a check there.
Examples of input/output that I need:
Input matrix: {{2,2,1,4},{4,1,2,2},{7,1,2,2},{2,10,2,1}}
Desired output: {1, 2, 2}
My output: {2, 2, 1, 0}
Input matrix: {{2,2,1,4},{4,1,3,2},{7,1,9,2},{2,10,2,1}}
Desired output: {1, 2}
My output: {2, 2, 1, 0}
public static int[] common_elements(int[][] matrix){
int[] final_array = new int[matrix.length];
for (int i = 0; i < matrix.length; i++) {
int counter = 0;
for (int j = 1; j < matrix.length; j++) {
for (int k = 0; k < matrix.length; k++) {
if(matrix.[0][i] == matrix.[j][k]){
counter += 1;
break;
}
}
}
if(counter == a.length-1){
final_array[i] = a[0][i];
}
}
return final_array;
}
EDIT: This is what I finally got together that fits my requirements and works flawlessly, with comments
public static int[] repetitiveInts(int[][] a){
//This is a method declared outside for sorting every row of the matrix ascending-ly before I do the element search.
for (int i = 0; i < a.length; i++) {
sorting(a[i]);
}
//Declaring a LinkedList in order to add elements on the go
LinkedList<Integer> final_list= new LinkedList<Integer>();
//Iterating through the matrix with every element of the first row, counting if it appears in every row besides the first one.
for (int i = 0; i < a.length; i++) {
int counter = 0;
for (int j = 1; j < a.length; j++) {
for (int k = 0; k < a.length; k++) {
//Checking if an element from the other rows match
if(a[0][i] == a[j][k]){
a[j][k] = a[0][i]-1; //If a match is found, the element is changed so finding duplicates is possible.
counter += 1;
break; //Breaking and checking the next row after one row checks out successfully.
}
}
}
//If the element is indeed in every row, adds it to the lit.
if(counter == a.length-1){
final_list.add(a[0][i]);
}
}
//Since I had to return a regular int[] array, converting the LinkedList into an array.
int[] final_realarray= new int[final_list.size()];
for (int i = 0; i < final_list.size(); i++) {
final_realarray[i] = final_list.get(i);
}
return final_realarray;
Grateful for help :)
The most efficient way to solve this problem is by creating a histogram of frequencies for each nested array in the matrix (i.e. determine the number of occurrences for every element in the nested array).
Every histogram will be represented by a Map<Integer, Integer> (array element as a key, its occurrences as a value). To generate a histogram only a single pass through the array is needed. In the solution below this logic resides inside the getFrequencies() method.
After creating all histograms we have to merge them. In terms of set theory we are looking for an intersection of keys in all histograms. I.e. we need only those keys that appear at least once in every histogram and a value for each key will be the smallest in all histograms for that key. This logic is placed in the getCommonElements().
In order to create a merged histogram, we can pick any of the histograms (in the code below the first histogram is used frequencies.get(0).keySet()) and iterate over its keys. Then in the nested loop, for every key, we need to find the minimum value associated with that in every histogram in a list (reminder: that will be the smallest number of occurrences for the key).
At the same time, while merging histograms we can also find the length of the resulting array by adding all the minimal frequencies together. That small optimization will allow to avoid doing the second iteration over the merged map.
The last step required is to populate the resulting array commonElements with keys from the merged histogram. Value of every key denotes how many times it has to be placed in the resulting array.
public static void main(String[] args) {
System.out.println(Arrays.toString(commonElements(new int[][]{{2,2,1,4},{4,1,2,2},{7,1,2,2},{2,10,2,1}})));
System.out.println(Arrays.toString(commonElements(new int[][]{{2,2,1,4},{4,1,3,2},{7,1,9,2},{2,10,2,1}})));
}
public static int[] commonElements(int[][] matrix){
List<Map<Integer, Integer>> frequencies = getFrequencies(matrix);
return getCommonElements(frequencies);
}
private static List<Map<Integer, Integer>> getFrequencies(int[][] matrix) {
List<Map<Integer, Integer>> frequencies = new ArrayList<>();
for (int[] arr: matrix) {
Map<Integer, Integer> hist = new HashMap<>(); // a histogram of frequencies for a particular array
for (int next: arr) {
// hist.merge(next, 1, Integer::sum); Java 8 alternative to if-else below
if (hist.containsKey(next)) {
hist.put(next, hist.get(next) + 1);
} else {
hist.put(next, 1);
}
}
frequencies.add(hist);
}
return frequencies;
}
private static int[] getCommonElements(List<Map<Integer, Integer>> frequencies) {
if (frequencies.isEmpty()) { // return an empty array in case if no common elements were found
return new int[0];
}
Map<Integer, Integer> intersection = new HashMap<>();
int length = 0;
for (Integer key: frequencies.get(0).keySet()) { //
int minCount = frequencies.get(0).get(key); // min number of occurrences of the key in all maps
for (Map<Integer, Integer> map: frequencies) {
int nextCount = map.getOrDefault(key, 0);
minCount = Math.min(nextCount, minCount); // getOrDefault is used because key might not be present
if (nextCount == 0) { // this key isn't present in one of the maps, no need to check others
break;
}
}
if (minCount > 0) {
intersection.put(key, minCount);
length += minCount;
}
}
int[] commonElements = new int[length];
int ind = 0;
for (int key: intersection.keySet()) {
int occurrences = intersection.get(key);
for (int i = 0; i < occurrences; i++) {
commonElements[ind] = key;
ind++;
}
}
return commonElements;
}
output
[1, 2, 2]
[1, 2]
Side note: don't violate the naming conventions, use camel-case for method and variable names.
Update
I've managed to implement a brute-force solution based on arrays and lists only as required.
The most important thing is that for this task you need two lists: one to store elements, another to store frequencies. Lists are bound together via indices. And these two lists are basically mimic a map, frankly saying a very inefficient one (but that's a requirement). Another possibility is to implement a class with two int fields that will represent the data for a common element, and then store the instances of this class in a single list. But in this case, the process of checking whether a particular element already exists in the list will be much more verbose.
The overall logic has some similarities with the solution above.
First, we need to pick a single array in the matrix (matrix[0]) and compare all its unique elements against the contents of all other arrays. Every element with non-zero frequency will be reflected in the list of elements and in the list of frequencies at the same index in both. And when the resulting array is being created the code relies on the corresponding indices in these lists.
public static int[] commonElements(int[][] matrix){
if (matrix.length == 0) { // case when matrix is empty - this condition is required because farther steps will lead to IndexOutOfBoundsException
return new int[0];
}
if (matrix.length == 1) { // a small optimization
return matrix[0];
}
// Map<Integer, Integer> frequencyByElement = new HashMap<>(); // to lists will be used instead of Map, because of specific requirement for this task
List<Integer> frequencies = new ArrayList<>(); // lists will be bind together by index
List<Integer> elements = new ArrayList<>();
int length = 0; // length of the resulting array
for (int i = 0; i < matrix[0].length; i++) {
if (elements.contains(matrix[0][i])) { // that means this element is a duplicate, no need to double-count it
continue;
}
int currentElement = matrix[0][i];
int minElementCount = matrix[0].length; // min number of occurrences - initialized to the max possible number of occurrences for the current array
// iterating over the all nested arrays in matrix
for (int row = 0; row < matrix.length; row++) {
int localCount = 0; // frequency
for (int col = 0; col < matrix[row].length; col++) {
if(matrix[row][col] == currentElement){
localCount++;
}
}
if (localCount == 0) { // element is absent in this array and therefore has to be discarded
minElementCount = 0;
break; // no need to iterate any farther, breaking the nested loop
}
minElementCount = Math.min(localCount, minElementCount); // adjusting the value the min count
}
// frequencyByElement.put(currentElement, minElementCount); // now we are sure that element is present in all nested arrays
frequencies.add(minElementCount);
elements.add(currentElement);
length += minElementCount; // incrementing length
}
return getFinalArray(frequencies, elements, length);
}
private static int[] getFinalArray(List<Integer> frequencies,
List<Integer> elements,
int length) {
int[] finalArray = new int[length];
int idx = 0; // array index
for (int i = 0; i < elements.size(); i++) {
int element = elements.get(i);
int elementCount = frequencies.get(i);
for (int j = 0; j < elementCount; j++) {
finalArray[idx] = element;
idx++;
}
}
return finalArray;
}
I'm new to Java, and I'm not sure how to ask the right question, so please bear with me. I have 40 total items of 6 different types to put into a new array; each item type has a different cost. The first item (quantity=1) costs $3, the second item (qty=2) costs $5 each, the third item (qty=4) costs $9 each, and so on. The quantity of each item type is in numTypeIndArray and the cost for each type is in costCSDriverArray. A cumulative count of the total items is in numTypeCumulArray.
So, the new array, indItemCostArray, should be single dimensional and have 40 elements. It would look something like {3,5,5,9,9,9,9,...,13,13,13}, but the last fifteen elements are a cost of $13. How do I get to this array with 40 elements? I started with trying to fill the array using a nested for loop but I haven't gotten there yet. The code below is plain wrong.
int[] costArray = new int[]{3,5,9,10,11,13};
int[] numTypeIndArray = new int[]{1,2,4,7,11,15};
int[] numTypeCumulArray = new int[]{1,3,7,14,25,40};
int[] indItemCostArray = new int[numTypeCumulArray[6]];
for (int i = 0; i < indItemCostArray.length; i++) {
for (int j = 0; j < numTypeIndArray[i]; j++) {
indItemCostArray[i+j] = costArray[j];
}
}
First of all, you'll get a ArrayOutOfBoundException at:
int[] indItemCostArray = new int[numTypeCumulArray[6]];
The size of the array numTypeCumulArray is 6, and arrays are 0 indexed. So, The last index number is 5, not 6, as indexing started from 0.
You can do as follows for accessing the last element of the array:
int[] indItemCostArray = new int[numTypeCumulArray[numTypeCumulArray.length - 1]];
Secondly, you're running your outer loop for 40 times and for each iteration your inner loop is trying to iterate for numTypeIndArray[i] times, where i is the iterator variable of outer loop. So, surely after sixth iteration, when value of i will be 6, your program will again throw the ArrayOutOfBoundException as you're accessing a value in the terminator condition of the inner loop from numTypeIndArray whose last index is 5.
Again, inside the inner loop, you're assigning indItemCostArray at index position i+j, which will actually far from your purpose.
To achieve what you are exactly expecting, you can do as follows:
int currentIndex =0;
for (int costIndex = 0; costIndex < costArray.length; costIndex++) {
for(int index = currentIndex; index < currentIndex + numTypeIndArray[costIndex]; index++) {
indItemCostArray[index] = costArray[costIndex];
}
currentIndex = numTypeCumulArray[costIndex];
}
Here, what I did is, in the outer loop I iterated the same amount of time the length of costArray, you can take the length of numTypeIndArray instead too, no issue. I've defined a variable named currentIndex to keep track of the current assignable index for array indItemCostArray. In the inner loop, I tried to begin with the currentIndex and loop upto the time same as the number of items needed for that type, given in numTypeIndArray[costIndex], and for each iteration, set the corresponding index of indItemCostArray with the cost of costIndex in the costArray. Finally, I update the currentIndex with the corresponding cumulative total items from numTypeCumulArray.
Hope you got everything clear.
The whole setup of three arrays is kind of weird. The weiredest is the third array. Think carefully, do you actually need it? You already have all the information in your second array. The third array can introduce a lot of unnacessary mistakes.
But, assuming that you actually need these arrays for some reason and there are no mistakes in making these arrays. You can get your required fourth array as follows,
int[] costArray = new int[]{3,5,9,10,11,13};
int[] numTypeIndArray = new int[]{1,2,4,7,11,15};
int[] numTypeCumulArray = new int[]{1,3,7,14,25,40};
// you want to make sure that your arrays are of same lenght
assert(costArray.length == numTypeIndArray.length && costArray.length == numTypeCumulArray.length);
// length of these arrays is unique items count
int uniqueItemsCount = costArray.length;
// totalItemsCount is last element of numTypeCumulArray
int totalItemsCount = numTypeCumulArray[uniqueItemsCount - 1];
int[] indItemCostArray = new int[totalItemsCount];
// use this to keep track of index in indItemCostArray
int itemCostIndex = 0;
for (int i = 0; i < uniqueItemsCount && itemCostIndex < totalItemsCount; i++) {
for (int j = 0; j < numTypeIndArray[i] && itemCostIndex < totalItemsCount; j++) {
indItemCostArray[itemCostIndex] = costArray[j];
// increase the index for next item cost
itemCostIndex += 1;
}
}
int[] costArray = new int[]{3,5,9,10,11,13};
int[] numTypeIndArray = new int[]{1,2,4,7,11,15};
int[] numTypeCumulArray = new int[]{1,3,7,14,25,40};
int[] indItemCostArray = new int[numTypeCumulArray[5]];
int num = 0;
for (int i = 0; i < numTypeIndArray.length; i++) {
for (int j = 0; j < numTypeIndArray[i]; j++) {
indItemCostArray[num + j] = costArray[i];
}
num += numTypeIndArray[i];
}
System.out.println(Arrays.toString(indItemCostArray));
First, you don't need int[] numTypeCumulArray = new int[]{1,3,7,14,25,40};
It just shows the cumulative values of the numTypeIndArray. The last value, 40 is just the sum of numTypeIndArray and that would be the size of the resulting array from your requirement.
It can be summed in a simple for loop or you can do it like this and then create the target array.
int maxSize = Arrays.stream(numTypeIndArray).sum();
int[] indItemCostArray = new int[maxSize];
Then you could proceed to populate the array with the values as has been shown. Here is another way using streams which you will undoubtedly learn about. The quick explanation is that it creates multiple streams of the proper quantities of cost.
e.g
stream1 -> {3}
stream2 -> {5,5};
stream3 -> {9,9,9,9} etc.
Then it flattens them in a single stream of those values and returns an array.
int[] result = IntStream.range(0, costArray.length)
.flatMap(i -> IntStream.range(0, numTypeIndArray[i])
.map(q -> costArray[i]))
.toArray();
But using a class to hold the information would be better. Here is one example.
class Product {
private String name;
private int cost;
private int quantity;
public Product(String name, int cost, int quantity) {
this.name = name;
this.cost = cost;
this.quantity = quantity;
}
public int getCost() {
return cost;
}
public int getQuantity() {
return quantity;
}
public String getName() {
return name;
}
#Override
public String toString() {
return new StringJoiner(", ","[", "]").add(name).add("cost="+cost).add("quantity="+quantity).toString();
}
}
And it can be used like so.
List<Product> products = new ArrayList<>();
for (int i = 0; i < costArray.length; i++) {
products.add(new Product("Item" + (i+1), costArray[i], numTypeIndArray[i]));
}
products.forEach(System.out::println);
Prints
[Item1, cost=3, quantity=1]
[Item2, cost=5, quantity=2]
[Item3, cost=9, quantity=4]
[Item4, cost=10, quantity=7]
[Item5, cost=11, quantity=11]
[Item6, cost=13, quantity=15]
And once again it can be streamed to create your results exactly as before only using the class getters to get the values.
int[] result2 = products.stream()
.flatMapToInt(
prod -> IntStream.range(0, prod.getQuantity())
.map(q -> prod.getCost()))
.toArray();
The two arrays result and result2 are identical. But you may find that using classes may eliminate the requirement for creating such an array.
so this method performs daily checks that adds one day to the displayedDays in order to determine the freshness of an item, so the method adds a day then check if it's rotten by calling the method isRotten() and if it's rotten it removes it the array
for (int i = 0; i < numItems; i++) {
items[i].displayedDays++;
}
for (int i = 0; i < numItems;) {
if (items[i].isRotten()) {
if (removeItem(i)) {
if (i > 0) {
i--;
}
continue;
}
}
i++;
}
this is also another method that uses the same loop and if's
so this method is supposed to remove the sweets from the array ( the array has two types of items bread and sweets)
double totalSweetsPrice = 0;
int count = numItems;
for (int i = 0; i < count;) {
Item item = items[i];
if (item instanceof Sweet) {
totalSweetsPrice += item.getPrice();
if (removeItem(i)) {
if (i > 0) {
i--;
}
continue;
}
}
i++;
}
and I don't understand the middle part and was hoping that there is a different loop or something whilst getting the same result
this was how a wrote the daily check method
for (int i = 0; i < numItems; i++) {
items[i].displayDays++ ;
if(items[i].isRotten())
removeItem(i); }
and the output was wrong
Most of the code stems from the fact you change the elements positions in an array you're enumerating. A different kind of data structure, more suited to the removal of elements, such as a linked list, would simplify greatly your code and would be more efficient.
As FooBar said, you can’t both iterate an ArrayList and remove items from that same ArrayList. So you need to separate the conditions. For example:
for (int i = 0; i < items.size(); i++) {
items.get(i).displayedDays++;
}
int i = 0:
while (i < items.size()) {
if(items.get(i).isRotten()) {
items.remove(i);
// don’t increment our counter here because now
// we’ve changed what object exists in the ArrayList
// at this index position
} else {
// only increment our counter if the item in that index
// is good, so we can now move on to check the next item
i++;
}
}
Why not use ArrayList.removeAll(Collection c)? This way, you can create a Collection, such as an ArrayList, iterate through the original list, add all rotten items to your new collection, then call removeAll on the original ArrayList after iterating through the entire list? That way, you don't have to worry about modifying the list while you're iterating through it.
I wrote below code to get duplicate elements from Arraylist. My aerospikePIDs list doesn't have any duplicate value but still when I am executing below code it is reading if condition.
ArrayList<Integer> aerospikePIDs = new ArrayList<Integer>();
ArrayList<Integer> duplicates = new ArrayList<Integer>();
boolean flag;
for(int j=0;j<aerospikePIDs.size();j++) {
for(int k=1;k<aerospikePIDs.size();k++) {
if(aerospikePIDs.get(j)==aerospikePIDs.get(k)) {
duplicates.add(aerospikePIDs.get(k));
flag=true;
}
if(flag=true)
System.out.println("duplicate elements for term " +searchTerm+duplicates);
}
}
Your inner loop should start from j + 1 (not from 1), otherwise when j = 1 (second iteration of j), for k = 1 (first iteration of k for j value equals to 1).
aerospikePIDs.get(j)==aerospikePIDs.get(k)
returns true.
So the code should be:
ArrayList<Integer> aerospikePIDs = new ArrayList<Integer>();
ArrayList<Integer> duplicates = new ArrayList<Integer>();
for (int j = 0; j < aerospikePIDs.size(); j++) {
for (int k = j + 1; k < aerospikePIDs.size(); k++) {
if (aerospikePIDs.get(j)==aerospikePIDs.get(k)) {
duplicates.add(aerospikePIDs.get(k));
System.out.println("duplicate elements for term " +searchTerm+duplicates);
}
}
}
Note: the flag is not necessary, because if you addeda duplicate you can print it directly in the if, without defining new unnecessary variables and code.
Use higher level abstractions:
Push all list elements into a Map<Integer, Integer> - key is the entry in your PIDs list, value is a counter. The corresponding loop simply checks "key present? yes - increase counter; else, add key with counter 1".
In the end, you can iterate that map, and each entry that has a counter > 1 ... has duplicates in your list; and you even get the number of duplicates for free.
And questions/answers that show you nice ways to do such things ... are posted here on almost daily basis. You can start here for example; and you only need to adapt from "String" key to "Integer" key.
Really: when working with collections, your first step is always: find the most highlevel way of getting things done - instead of sitting down and writing such error-prone low-level code as you just did.
You are iterating using the same arraylist. You are checking every data in inner for loop, for sure it will display duplicates.
I've been struggling to create a function to essentially find all the indices of duplicate elements in a multi-dimensional array(unsorted), in this case a 5x5 array, and then using the indices found changing the parallel elements in a score array. But only find duplicates within columns and not comparatively to the other columns in the array Here is what I've done so far, with research online. The main problem with this code is that it will find all the duplicate elements but not the originals. For example: if the array holds the elements:
{{"a","a","a"},{"b","b","b"},{"a","c","a"}}, then it should change the parallel score array to: {{0,1,0},{1,1,1},{0,1,0}}. But instead it only recognizes the last row and top the top row's duplicates.
Code:
public static void findDuplicates(String a[][])
{
System.out.println("*Duplicates*");
Set set = new HashSet();
for(int j = 0; j<a.length; j++)
{
for(int i=0; i < a[0].length; i++)
{
if(!set.contains(a[i][j]))
{
set.add(a[i][j]);
}
else
{
System.out.println("Duplicate string found at index " + i + "," + j);
scores[i][j] -= scores[i][j];
}
}
set = new HashSet();
}
}
I know my explanation is a bit complicated, but hopefully it is understandable enough. Thanks,
Jake.
Your logic is incorrect. Your outer loop is j and inner loop is i but you're doing:
set.add(a[i][j]);
It should be the other way around:
set.add(a[j][i]);
Technically you could get an out of bounds exception if the array isn't NxN. But you can state that as a precondition.
For some reason you're also setting to 0 with:
scores[i][j] -= scores[i][j];
Why not just:
scores[i][j] = 0;
But to find duplicates within columns:
public static void findDuplicates(String a[][]) {
for (int col=0; col<a[0].length; col++) {
Map<String, Integer> values = new HashMap<String, Integer>();
for (int row=0; row<a.length; row++) {
Integer current = values.put(a[row][col], row);
if (current != null) {
scores[row][col] = 0;
scores[current][col] = 0;
}
}
}
}
How does this work?
I've renamed the loop variables to row and col. There's no reason to use i and j when row and col are far more descriptive;
Like you I assume the input array is correct as a precondition. It can be NxM (rather than just NxN) however;
I use a Map to store the index of each value. Map.put() returns the old value if key is already in the Map. If that's the case you've found a duplicate;
The current (row,col) and (current,col) are set to 0. Why subtract the score from itself rather than simply setting to 0?
if the value "a" is found 3+ times in a column then scores[current][col] will be set to 0 more than once, which is unnecessary but not harmful and makes for simpler code.
I've declared the Map using generics. This is useful and advisable. It says the Map has String keys and Integer values, which saves some casting;
It also uses auto-boxing and auto-unboxing to convert an int (the loop variable) to and from the wrapper class Integer.