Sigma
Sigma

Reputation: 221

C#: how to detect repeating values in an array and process them in such a way that each repeating value is only processed once?

I wrote this simple program:

    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("Number of elements in the array: ");
            int numberOfElements = int.Parse(Console.ReadLine());
            int[] array = new int[numberOfElements];
            for(int i = 0; i < numberOfElements; i++)
            {
                Console.Write($"Element no {i+1}: ");
                array[i] = int.Parse(Console.ReadLine());
            }
            for(int i = 0; i < array.Length; i++)
            {
                int count = 0;
                for(int j = 0; j < array.Length; j++)
                {
                    if(array[i] == array[j])
                    {
                        count++;
                    }
                }
                Console.WriteLine($"{array[i]} appears " + count + " times");
            }
        }
    }
}

Is there any option to make the displayed values print only once? For example, if there are three occurrences - the message displays three times. Is it possible to make it display once when there are more occurrences though?

Upvotes: 20

Views: 1701

Answers (7)

TheGeneral
TheGeneral

Reputation: 81493

You could use a GroupBy instead of the for loop

Groups the elements of a sequence.

var results = array
     .GroupBy(x => x)
     .Select(x => new {Value = x, Count = x.Count()});

foreach(var g in results)
   Console.WriteLine($"{g.Value} appears {g.Count} times");

Or another way it to use a HashSet to keep track of what you have displayed. A HashSet is basically a collection that contains no duplicate elements. The Add methods returns true if it can add an element or false otherwise

HashSet<T>.Add(T) Method

returns true if the element is added to the HashSet object; false if the element is already present.

var hashSet = new HashSet<int>();
for (int i = 0; i < array.Length; i++)
{
    int count = 0;
    for (int j = 0; j < array.Length; j++)
        if (array[i] == array[j])
            count++;

    // Add to the hashset, if the add method returns true, 
    // it means the value was uniquely added, ergo you have not displayed yet
    if (hashSet.Add(array[i]))
        Console.WriteLine($"{array[i]} appears " + count + " times");
}

Full Demo Here


Or another approach is to use a Dictionary. The premise is to iterate over the array, try an add each item to the dictionary with TryAdd if it's already found increment the value

var dictionary = new Dictionary<int,int>();
foreach(var item in array)
    if(!dictionary.TryAdd(item,1))
        dictionary[item]++;
    
foreach(var item in dictionary)
    Console.WriteLine($"{item.Key} appears {item.Value} times");

Full Demo Here

Upvotes: 12

Alex Leo
Alex Leo

Reputation: 2851

The first idea I had was the same of the comment from Jon Skeet, since the simplicity it implies.

The idea is to set null for the value we have already counted (matched).

From a developer point of view it is very simple and doesn't deviate too much from the OP's code.

        Console.Write("Number of elements in the array: ");
        int numberOfElements = int.Parse(Console.ReadLine());
        int?[] array = new int?[numberOfElements];
        for (int i = 0; i < numberOfElements; i++)
        {
            Console.Write($"Element no {i + 1}: ");
            array[i] = int.Parse(Console.ReadLine());
        }

        for (int i = 0; i < array.Length; i++)
        {
            int count = 0;
            int? current = array[i];

            if (array[i] != null)
            {
                for (int j = 0; j < array.Length; j++)
                {
                    if (current == array[j])
                    {
                        count++;
                        array[j] = null;
                    }
                }
                Console.WriteLine($"{current} appears " + count + " times");
            }
        }

int?[] defines a nullable value type. Therefore each item in the array can have either a null or int value - documentation here.

Upvotes: 8

Yaroslav Bres
Yaroslav Bres

Reputation: 1317

You can also use Lookup here:

            var sb = new StringBuilder();
            foreach (var value in array.ToLookup(item => item))
            {
                sb.AppendLine($"{value.Key} appears " + value.Count() + " times");
            }
            
            Console.WriteLine(sb.ToString());

Upvotes: 3

Miraziz
Miraziz

Reputation: 509

In the inner loop, try to check if there were already any occurrences of the current element until you exceed the outer index.

for(int i = 0; i < array.Length-1; i++)
            {
                int count = 1;
                bool appeared = false;
                for(int j = 0; j < array.Length; j++)
                {
                    // until we are not at the same index as the outer loop
                    // check if we haven't already met the current element
                    if(j < i)
                    {
                        if (array[i] == array[j])
                        {
                            // set current value appearance to true
                            // to know if current element should be displayed
                            appeared = true;

                            // break the loop because there is no sense of continuing
                            // current look
                            break;
                        }   
                    }

                    // if we are ahead of outer index
                    // check if there are occurences of the element
                    else if(j > i)
                    {
                        if (array[i] == array[j])
                            count++;
                    }
                }

                // don't print the current element if it has appeared before
                if(!appeared)
                    Console.WriteLine($"{array[i]} appears {count} times");
            }

I believe there should be a more optimal solution, as this one's time complexity is linear... You can think of some optimization. For example, you can store occurred elements in the array and check through the array at each iteration, so you don't need to start the inner loop from the beginning, but instead start it from the outer loop's position + 1 but it's also not the best solution.

P.S check out about string interpolation, because you don't need to concatenate strings when you use it.

Upvotes: 3

Michał Turczyn
Michał Turczyn

Reputation: 37367

Other approach more suited for you would be too take only unique values from array, i.e.:

var unique = array.Distinct().ToArray();

for (int i = 0; i < unique.Length; i++)
{
    int count = 0;
    for (int j = 0; j < array.Length; j++)
    {
        if (array[i] == array[j])
        {
            count++;
        }
    }
    Console.WriteLine($"{unique[i]} appears " + count + " times");
}

Upvotes: 3

Johnathan Barclay
Johnathan Barclay

Reputation: 20353

One simple way would be to swap your outer for loop with a foreach using a set to obtain distinct values.

So replace this:

for (int i = 0; i < array.Length; i++)

With this:

foreach (int i in new HashSet<int>(array))

And this:

if (array[i] == array[j])

With this:

if (i == array[j])

Upvotes: 4

Vimal CK
Vimal CK

Reputation: 3563

An approach using Dictionary with O(n) complexity.

Console.Write("Number of elements in the array: ");

int numberOfElements = int.Parse(Console.ReadLine());
var dictionary = new Dictionary<int, int>();

for (int i = 0; i < numberOfElements; i++)
{
    Console.Write($"Element no {i + 1}: ");
    var value = int.Parse(Console.ReadLine());
    if (!dictionary.ContainsKey(value)) dictionary.Add(value, 0);

    dictionary[value] = dictionary[value] + 1;
}

foreach (var item in dictionary)
{
    Console.WriteLine($"{item.Key} appears {item.Value} times");
}

Upvotes: 5

Related Questions