Home > Back-end >  Returning a string from an IEnumerable<T>function
Returning a string from an IEnumerable<T>function

Time:11-20

I am probably doing something wrong but I'm trying to do this Kata on Codewars

This is my current code below.

public static class Kata
{
 public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> arr) 
 {
   Type t = typeof(T);
   if (t == typeof(string))
     return (IEnumerable<T>)String.Join("",arr.Distinct()).AsEnumerable();
   
   return arr.Distinct().ToArray();
 }
}

The unit tests for this kata are expecting an input of "AAAABBBCCDAABBB" to be returned as "ABCDAB".

My code above is failing due to this error Expected is <System.String>, actual is <System.Char[6]>

If I try to return a string I get this error: error CS0029: Cannot implicitly convert type 'string' to 'System.Collections.Generic.IEnumerable<T>'

I'm lost as how I can return the expected string if I can't return a string (and a char array fails)

Thanks

CodePudding user response:

You can implement it like this (since you've used <T> let implement general case solution with custom comparer):

public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> source, 
                                              IEqualityComparer<T> comparer = null) {
  if (null == source)
    throw new ArgumentNullException(nameof(source));

  comparer ??= EqualityComparer<T>.Default;

  bool first = true;
  T prior = default; // default: let compiler be happy

  foreach (T item in source)
    if (first || !comparer.Equals(item, prior)) {
      prior = item;
      first = false;

      yield return item;
    }
}

Here we just check if current item is equal to prior.

Demo:

string source = "AAAABBBCCDAABBB";

string result = string.Concat(UniqueInOrder(source)); 

Console.Write(result);

Outcome:

ABCDAB

Edit: Please, note that Distinct() in the

arr.Distinct()

removes duplicates in the entire arr, that's why you'll get only 4 distinct characters:

AAAABBBCCDAABBB  ->  ABCD
             Distinct()

In the given problem we have a weaker condition: current item must not be equal to prior.

CodePudding user response:

This should do it:

public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable) 
{
    using var e = iterable.GetEnumerator();
    bool EnumeratorActive = e.MoveNext(); //start iterating
    while (EnumeratorActive)
    {
        // set and yield the current value
        T cur = e.Current; 
        yield return cur;

        // keep advancing while the iterator is active and additional values match the current val
        while(cur.Equals(e.Current) && (EnumeratorActive = e.MoveNext()))
        {} //empty body intentional
    }    
}

It uses a pattern of nested while loops called Control/Break that still runs in linear time (because only the inner loop advances).

Here's a reduced version without the extra explainers:

public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable) 
{
    using var e = iterable.GetEnumerator();
    bool EnumeratorActive = e.MoveNext();
    while (EnumeratorActive)
    {
        T cur = e.Current; 
        yield return cur;
        while(cur.Equals(e.Current) && (EnumeratorActive = e.MoveNext()));
    }    
}
  • Related