2013-11-27 11 views
6

Mam listę. Chciałbym wziąć ostatnią wartość z każdego przebiegu podobnych elementów.Jak wybrać ostatnią wartość z każdego uruchomienia podobnych elementów?

Co mam na myśli? Pozwolę sobie podać prosty przykład. Biorąc pod uwagę listę słów

[ 'golf', 'hip', 'hop', 'hotelu', 'trawa', 'świata', 'wee']

a funkcja podobieństwa 'począwszy od tej samej litery', funkcja zwróci krótszą listę

[ 'golf', 'Hotel', 'trawa', 'wee']

Dlaczego? Oryginalna lista zawiera 1-bieg słów G, 3-biegowe słowa H, 1-etap słów G i 2-ciąg słów W. Funkcja zwraca ostatnie słowo z każdego przebiegu.

Jak mogę to zrobić?


Hipotetyczny C# składni (w rzeczywistości pracuję z obiektami klienta, ale chciałem podzielić się czymś można uruchomić i przetestować samemu)

> var words = new List<string>{"golf", "hip", "hop", "hotel", "grass", "world", "wee"}; 
> words.LastDistinct(x => x[0]) 
["golf", "hotel", "grass", "wee"] 

Edit: Próbowałem .GroupBy(x => x[0]).Select(g => g.Last()) ale że daje ['trawa', 'hotel', 'wee'], który jest nie czego chcę. Przeczytaj uważnie przykład.


Edytuj. Inny przykład.

[ 'jabłka', 'armie', 'czarny', 'piwo', 'Bastion', 'kot', 'koszyk', 'móc', 'sztuka', 'Kora']

Tutaj znajduje się 5 tras (run związku A, w odległości bieg B, bieg z C, w odległości nowy bieg towarzyszącej, nowy przebiegu B). Ostatnie słowo z każdego biegu będzie:

[ „wojsko”, „bastion”, „koszyk”, „sztuka”, „Kora”]

Ważne jest, aby zrozumieć, że każdy bieg jest niezależny. Nie mieszaj biegu A na początku z biegiem A na końcu.

+0

co jest specyficzne wyjście trzeba? – dbw

+0

Więc nie chcesz grupować tylko według pierwszej litery, ale także kolejnych słów? –

+0

Spodziewam się zobaczyć '['hotel', 'grass', 'wee']' bez 'golfa' –

Odpowiedz

0

poszedłem z

/// <summary> 
/// Given a list, return the last value from each run of similar items. 
/// </summary> 
public static IEnumerable<T> WithoutDuplicates<T>(this IEnumerable<T> source, Func<T, T, bool> similar) 
{ 
    Contract.Requires(source != null); 
    Contract.Requires(similar != null); 
    Contract.Ensures(Contract.Result<IEnumerable<T>>().Count() <= source.Count(), "Result should be at most as long as original list"); 

    T last = default(T); 
    bool first = true; 
    foreach (var item in source) 
    { 
     if (!first && !similar(item, last)) 
      yield return last; 

     last = item; 
     first = false; 
    } 

    if (!first) 
     yield return last; 
} 
0

Możesz użyć następującej metody rozszerzania, aby podzielić sekwencję na grupy (np.sub-sequnces) przez pewien warunek:

public static IEnumerable<IEnumerable<T>> Split<T, TKey>(
    this IEnumerable<T> source, Func<T, TKey> keySelector) 
{ 
    var group = new List<T>(); 

    using (var iterator = source.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
      yield break; 
     else 
     { 
      TKey currentKey = keySelector(iterator.Current); 
      var keyComparer = Comparer<TKey>.Default; 
      group.Add(iterator.Current); 

      while (iterator.MoveNext()) 
      { 
       var key = keySelector(iterator.Current); 
       if (keyComparer.Compare(currentKey, key) != 0) 
       { 
        yield return group; 
        currentKey = key; 
        group = new List<T>(); 
       } 

       group.Add(iterator.Current); 
      } 
     } 
    } 

    if (group.Any()) 
     yield return group;   
} 

I zaczyna swój oczekiwanych wyników wygląda następująco:

string[] words = { "golf", "hip", "hop", "hotel", "grass", "world", "wee" }; 

var result = words.Split(w => w[0]) 
        .Select(g => g.Last()); 

Wynik:

golf 
hotel 
grass 
wee 
+0

To nie jest literówka, przeczytaj mój komentarz. –

1

Można użyć tego rozszerzenia, które można grupować według sąsiaduje/kolejne elementy:

public static IEnumerable<IGrouping<TKey, TSource>> GroupAdjacent<TSource, TKey>(
    this IEnumerable<TSource> source, 
    Func<TSource, TKey> keySelector) 
{ 
    TKey last = default(TKey); 
    bool haveLast = false; 
    List<TSource> list = new List<TSource>(); 
    foreach (TSource s in source) 
    { 
     TKey k = keySelector(s); 
     if (haveLast) 
     { 
      if (!k.Equals(last)) 
      { 
       yield return new GroupOfAdjacent<TSource, TKey>(list, last); 
       list = new List<TSource>(); 
       list.Add(s); 
       last = k; 
      } 
      else 
      { 
       list.Add(s); 
       last = k; 
      } 
     } 
     else 
     { 
      list.Add(s); 
      last = k; 
      haveLast = true; 
     } 
    } 
    if (haveLast) 
     yield return new GroupOfAdjacent<TSource, TKey>(list, last); 
} 

public class GroupOfAdjacent<TSource, TKey> : IEnumerable<TSource>, IGrouping<TKey, TSource> 
{ 
    public TKey Key { get; set; } 
    private List<TSource> GroupList { get; set; } 
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return ((System.Collections.Generic.IEnumerable<TSource>)this).GetEnumerator(); 
    } 
    System.Collections.Generic.IEnumerator<TSource> System.Collections.Generic.IEnumerable<TSource>.GetEnumerator() 
    { 
     foreach (var s in GroupList) 
      yield return s; 
    } 
    public GroupOfAdjacent(List<TSource> source, TKey key) 
    { 
     GroupList = source; 
     Key = key; 
    } 
} 

Wtedy to łatwe:

var words = new List<string>{"golf", "hip", "hop", "hotel", "grass", "world", "wee"}; 
IEnumerable<string> lastWordOfConsecutiveFirstCharGroups = words 
      .GroupAdjacent(str => str[0]) 
      .Select(g => g.Last()); 

wyjściowa:

string.Join(",", lastWordOfConsecutiveFirstCharGroups); // golf,hotel,grass,wee 

Twoja druga próbka:

words=new List<string>{"apples", "armies", "black", "beer", "bastion", "cat", "cart", "able", "art", "bark"}; 
lastWordOfConsecutiveFirstCharGroups = words 
    .GroupAdjacent(str => str[0]) 
    .Select(g => g.Last()); 

wyjściowa:

string.Join(",", lastWordOfConsecutiveFirstCharGroups); // armies,bastion,cart,art,bark 

Demonstration

1

Nic zbyt skomplikowane z po prostu robi to staromodny sposób:

Func<string, object> groupingFunction = s => s.Substring(0, 1); 
IEnumerable<string> input = new List<string>() {"golf", "hip", "..." }; 

var output = new List<string>(); 

if (!input.Any()) 
{ 
    return output; 
} 

var lastItem = input.First(); 
var lastKey = groupingFunction(lastItem); 
foreach (var currentItem in input.Skip(1)) 
{ 
    var currentKey = groupingFunction(str); 
    if (!currentKey.Equals(lastKey)) 
    { 
     output.Add(lastItem); 
    } 
    lastKey = currentKey; 
    lastItem = currentItem; 
} 

output.Add(lastItem); 

Można również włączyć to do ogólnej metody wydłużania jako Tim Schmelter has done; Podjąłem już kilka kroków, aby uogólnić kod celowo (przy użyciu klucza typu: object i IEnumerable<T> jako typ wejścia).

0

Spróbuj algorytm

 var words = new List<string> { "golf", "hip", "hop", "hotel", "grass", "world", "wee" }; 
     var newList = new List<string>(); 
     int i = 0; 
     while (i < words.Count - 1 && i <= words.Count) 
     { 
      if (words[i][0] != words[i+1][0]) 
      { 
       newList.Add(words[i]); 
       i++; 
      } 
      else 
      { 
       var j = i; 
       while (j < words.Count - 1 && words[j][0] == words[j + 1][0]) 
       { 
        j++; 
       } 
       newList.Add(words[j]); 
       i = j+1; 
      } 
     } 
0

Ponieważ wejście jest lista <>, więc myślę, że to powinno działać dla ciebie z akceptowalnej wydajności i przede wszystkim jest to bardzo zwięzły:

var result = words.Where((x, i) => i == words.Count - 1 || 
            words[i][0] != words[i + 1][0]); 

Można dopisz ToList() na wynik, aby uzyskać List<string>, jeśli chcesz.

Powiązane problemy