1

I've been trying to write code that finds the unique values in a sorted array that also has duplicates.

So far, I've written:

public static int numUnique (double[] list) {
    int counter = 0;

    if (Array.getLength(list) < 1) {
            return Length.list);
        }
    else{
        for (int i=0; i < Length.list); i++){
            for (int j=i+1; j< Length.list); j++){
                if (list[i] != list[j]){
                    newrArray[i] = list[i];
                    counter++;

                }
            }
        }
    }

    return counter;
}

Input:

{31, 31, 31, 31, 33, 46, 46, 46, 46, 46, 52, 65, 65, 66, 75, 98, 98}

Expected output:

8

I cannot use HashSets or ArrayLists. I think the only viable option is copying from one array to another and then counting what is in the new array (assuming that only unique values are copied into the new array).

4
  • Are there any constraints? E.g. max number is 100 or something? Commented Jan 16, 2017 at 20:47
  • 1
    Copy into a new array, sort, and count the number of elements not equal to the preceding element. Commented Jan 16, 2017 at 20:47
  • 1
    Then skip the copying and sorting. Commented Jan 16, 2017 at 20:49
  • Possible duplicate of Java Arrays: Finding Unique Numbers In A Group of 10 Inputted Numbers Commented Jan 18, 2017 at 7:07

7 Answers 7

3

Do you know the maximum value in this array? If it's small enough, you can make a boolean array of that size and set the value to true if you find that value in the original array.

This is called counting sort.

Example:

boolean[] found = new boolean[max];

for(int i : list)
    found[i] = true;
    
int unique = 0;
for(int i = 0; i < found; i++)
    if(found[i]) unique++;

If not, count the number of unique elements and insert them.

public int uniqueAmount(double[] list) {
    double last = Double.NaN;
    int unique = 0;
    for(int i = 0; i < list.length; i++)
        if(last != (last = list[i]))
            unique++;
    return unique;
}

public double[] uniqueValues(double[] list) {
    int unique = uniqueAmount(list);
    double[] found = new double[unique];
    double last = Double.NaN;
    last = list[0];
    found[0] = last;
    for(int i = 0, index = 1; i < list.length; i++)
        if(last != list[i]) {
            found[index++] = list[i];
            last = list[i];
        }
    return found;
}

Tested and it works. Returns 8 if you call uniqueAmount and the array [31.0, 33.0, 46.0, 52.0, 65.0, 66.0, 75.0, 98.0] if you call uniqueValues (as requested in your edit).

Sign up to request clarification or add additional context in comments.

1 Comment

@curiousjazz77 fixed it and tested this time, works for me
1

You can also use stream to do this. I create an IntStream in range from first index to last in the array. Then I filter elements using "indexOf(elem)" method that are first occurrances of numbers in the array. After that, using "mapToObj()" i can get appropriate elements and using "count()" get their amount.

For example:

List<Integer> d = Arrays.asList(31, 31, 31, 31, 33, 46, 46, 46, 46, 46, 52, 65, 65, 66, 75, 98, 98);
long result = IntStream.range(0, d.size())
                       .filter(a -> a == d.indexOf(d.get(a)))
                       .mapToObj(d::get)
                       .count();
System.out.println(result);

7 Comments

This is using a side effect in a stream. See side-effects here: docs.oracle.com/javase/8/docs/api/java/util/stream/…
Thank you, I haven't heard about side-effects before. Could you tell me which part of my code is a side-effect?
You're modifying state inside the filter's lambda expression i.e. opm.add(a).
Thank you for your note, I changed it into better solution.
The -1 shouldn't be there because end is exclusive docs.oracle.com/javase/8/docs/api/java/util/stream/… It's only working because the last two numbers are the same.
|
0

You're overcomplicating things quite a bit:

Since the array is sorted, all you need to do is check whether a value and the value before it in the array are equal. If they aren't increment the count, else continue with the next value:

int uniqueNum(double[] d){
    if(d.length < 2)
        return d.length;

    int count = 0;

    //choose an initial previous value that differs from the first element in the array
    double prev = (d[0] == 1.0 ? 2.0 : 1.0);
    for(double v : d){
        //ignore duplicate values
        if(v == prev)
            continue;

        count++;
        prev = v;
    }

    return count;
}

This works since in a sorted array duplicate values will always form a sequence.

Why your code doesn't work:

for (int i=0; i < Array.getLength(list); i++){
    for (int j=i+1; j< Array.getLength(list); j++){
        if (list[i] != list[j]){
            catcherArray[i] = list[i];
            counter++;
        }
    }
}

This codes doesn't count the number of distinct values in the array, but for any index in the array the number of values that are different from the value at that index.

Comments

0

There is no need to copy any arrays or use extra data structures since you are given the array in sorted order. That means when list[i] != list[i+1] there won't be any more occurrences in the array. This helps you greatly and allows you to do a single traverse of the array in order to find a solution. Here is the simple solution with no extra collections

public static int FindTotalUniqueNumbers(double[] list)
{
    if(list.length < 0)
        return 0;

    double currentNumber = list[0];
    int currentCount = 1;
    for(int i = 1; i < list.length; i++)
    {
        if(list[i] != currentNumber)
        {
            currentCount++;
            currentNumber = list[i];
        }
    }

    return currentCount;
}

Example

double[] list = new double[] {31, 31, 31, 31, 33, 46, 46, 46, 46, 46, 52, 65, 65, 66, 75, 98, 98};
System.out.println(FindTotalUniqueNumbers(list));

Output

8

Comments

0

The array is sorted, which means the duplicate values are next to each other. It's good news for us because once we see a different value than the previous, we know for sure that the previous value is not going to show up again, ever. We can do a linear scan:

int countDistinct(double [] numbers) {
    if (numbers == null || numbers.length == 0) return 0;

    int c = 1, n = numbers.length;

    // The previous value, initialized to the first element
    double prev = numbers[0];

    // Start loop from the second element
    for (int i = 1; i < n; i++) {
        if (prev != numbers[i]) {
            c++;
            prev = numbers[i];
        }
    }

    return c;
}

Comments

0

Since this code snippet does not require you to sort the array, considering that the elements are sorted, a more efficient version of this can be written. However, this is efficient for unsorted arrays.

public static int numUnique(double[] list) {
    double[] tempArray = new double[0];
    int index;
    for (double element : list) {
        boolean matchFound = false;
        if (tempArray.length > 0) {
            for (double d : tempArray) {
                if (d == element) {
                    matchFound = true;

                    break;
                }
            }
        }
        if (!matchFound) {
            tempArray = Arrays.copyOf(tempArray, tempArray.length + 1);
            tempArray[tempArray.length - 1] = element;
        }
    }
    return tempArray.length;
}

2 Comments

Welcome to Stack Overflow! Code-only answers are not very helpful. Please edit your answer to explain why your code solves the original problem.
No, the array size will get increase automatically in the program based on the no of unique numbers in the original array. No need to use ArrayList. The program does not use any array list.
0

You can you use IntStream distinct() or DoubleStream distinct() based on your array.

double[] doubleArray ={31.0, 31.0, 31.0, 31.0, 33.0, 46.0, 46.0, 46.0, 46.0, 46.0, 52.0, 65.0, 65.0, 66.0, 75.0, 98.0, 98.0};
long count = DoubleStream.of(doubleArray).distinct().count();
System.out.println(count);

Output:

8

If you have int array you can use IntStream distinct()

int[] intArray = {31, 31, 31, 31, 33, 46, 46, 46, 46, 46, 52, 65, 65, 66, 75, 98, 98};
long count = IntStream.of(intArray).distinct().count();
System.out.println(count);

Output:

8

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.