tags:

views:

353

answers:

7

Hi,

I need help with answering the following question..I have an list: int list = { 1,1,2,3,4,4,5,7,7,7,10};

Now i need to make a program which calculates the double numbers. A number is double when the number before it is the same..I hope you understand..:P So 1 is double, 4 is doubles and we got 2 double in 7,7,7.

I hope you can help, Thanks..

+24  A: 

Here's a solution in LINQ:

var doubles = list.Skip(1)
                  .Where((number, index) => list[index] == number);

This creates another sequence by skipping the first member of the list, and then finds elements from both sequences that have the same index and the same value. It will run in linear time, but only because a list offers O(1) access by index.

Ani
A definitely +1. The answer is concise, correct (not tested, but I'm taking the risk), very clever, and correctly argued why it runs in linear time.
Fede
+1: That's very elegant!
RedFilter
Imagine copying that in as a homework answer, then having to explain it to the class (and the teacher) ... mwahahaha
James Westgate
+1: Very smart!
Dmitry Ornatsky
+2  A: 

something like this may work:

list.GroupBy (l => l).Where (l => l.Count () > 1).SelectMany (l => l).Distinct();

EDIT:

the above code doesn't get the result the OP wanted. Here is an edited version that is taking inspiration from Ani's elegant solution below: :)

list.GroupBy(l => l).Select(g=>g.Skip(1)).SelectMany (l => l);
KJN
+6  A: 

Here's an approach which is relatively simple, only iterates once over the sequence, and works with any sequence (not just lists):

public IEnumerable<T> FindConsecutiveDuplicates<T>(this IEnumerable<T> source)
{
    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            yield break;
        }
        T current = iterator.Current;
        while (iterator.MoveNext())
        {
            if (EqualityComparer<T>.Default.Equals(current, iterator.Current))
            {
                yield return current;
            }
            current = iterator.Current;
        }
    }
}

Here's another one which is even simpler in that it's only a LINQ query, but it uses side-effects in the Where clause, which is nasty:

IEnumerable<int> sequence = ...;

bool first = true;
int current = 0;
var result = sequence.Where(x => {
   bool result = !first && x == current;
   current = x;
   first = false;
   return result;
});

A third alternative, which is somewhat cleaner but uses a SelectConsecutive method which is basically SelectPairs from this answer, but renamed to be slightly clearer :)

IEnumerable<int> sequence = ...;
IEnumerable<int> result = sequence.SelectConsecutive((x, y) => new { x, y })
                                  .Where(z => z.x == z.y);
Jon Skeet
What do you mean "uses a side-effect"?
Lasse V. Karlsen
My eyes are bleeding.
Winston Smith
@Lasse: Good point. Um, I changed my plan. Hang on, and I'll put the side-effecting version back up :)
Jon Skeet
+5  A: 

Everyone seems to be trying to find good ways of doing it, so here's a really bad way instead:

List<int> doubles = new List<int>();
Dictionary<int, bool> seenBefore = new Dictionary<int, bool>();

foreach(int i in list)
{
    try
    {
        seenBefore.Add(i, true);
    }
    catch (ArgumentException)
    {
        doubles.Add(i);
    }
}

return doubles;

Please don't do it like that.

teedyay
haha, +1 for sense of humor. It's friday after all
Isak Savo
Thanks. I wasn't sure if I'd get down-votes for posting a bad answer or up-votes for saying it was bad. :-)
teedyay
+1 Its not an entirely bad non-Linq answer apart from the exception - you could use ContainsKey or TryGetValue to avoid the exception and it would be just fine.
James Westgate
True, but I was only using a Dictionary so I could get an exception thrown. :-)
teedyay
A `HashSet` would be of great use here.
NickLarsen
I considered a HashSet, but it doesn't throw the Exception I wanted. :p
teedyay
A: 

Here you go with the answer in c# :)

int[] intarray = new int[] { 1, 1, 2, 3, 4, 4, 5, 7, 7, 7, 10 };

int previousnumber = -1;
List<int> doubleDigits = new List<int>();
for (int i = 0; i < intarray.Length; i++)
{
    if (previousnumber == -1) { previousnumber = intarray[i]; continue; }
    if (intarray[i] == previousnumber)
    {
        if (!doubleDigits.Contains(intarray[i]))
        {
            doubleDigits.Add(intarray[i]);
            //Console.WriteLine("Duplicate int found - " + intarray[i]);
            continue;
        }
    }
    else
    {
        previousnumber = intarray[i];
    }
}
Rajesh
A: 

An example that (probably) performs better than using Linq, though is arguably less elegant:

for (int i = 1; i < list.Count; i++)
    if (list[i] == list[i - 1])
        doubles.Add(list[i]);
teedyay
A: 

You could do this:

list.GroupBy(i => i).Where(g => g.Count() > 1).SelectMany(g => g.Skip(1))

This is a bit like @KJN's answer, except I think it expresses the "doubles" and "two doubles" clause in the question a bit better:

  1. group all integers together
  2. only interested in those that appear more than once (g.Count() > 1)
  3. select a flattened list of the "doubles", being those after the first (g.Skip(1))

PS: We are assuming here, that GroupBy doesn't first sort the list and if it does, that that sort is not negatively influenced by a pre-sorted list...

Daren Thomas