2010-12-16 4 views
11
var nums = new[]{ 1, 2, 3, 4, 5, 6, 7}; 
var pairs = /* some linq magic here*/ ; 

=> pary = {{1, 2}, {3, 4}, {5, 6}, {7, 0}}LINQ obiektów - powrót par numerów z listy numerów

Elementy pairs powinny być listami dwuelementowymi lub instancjami anonimowej klasy z dwoma polami, np. new {First = 1, Second = 2}.

+1

dokładną kopią pytanie zadane przez siebie http: // stackoverflow.com/questions/3575925/linq-to-return-all-pairs of-elements-from-two-lists –

+0

@Jani Nie, nie jest. Prosi o odpowiednik metody Zip() Pythona (lub Rubiego) -> bierze dwie listy i tworzy jedną listę krotek. To pytanie dotyczy partycjonowania pojedynczej listy. –

+2

@Cristi ma, przepraszam –

Odpowiedz

3

Spróbuj tego:

int i = 0; 
var pairs = 
    nums 
    .Select(n=>{Index = i++, Number=n}) 
    .GroupBy(n=>n.Index/2) 
    .Select(g=>{First:g.First().Number, Second:g.Last().Number}); 
+4

możesz zrobić. Wybierz ((n, i) => ...), aby uzyskać automatyczny licznik –

0
var nums = new float[] { 1, 2, 3, 4, 5, 6, 7 }; 
var enumerable = 
     Enumerable 
      .Range(0, nums.Length) 
      .Where(i => i % 2 == 0) 
      .Select(i => 
      new { F = nums[i], S = i == nums.Length - 1 ? 0 : nums[i + 1] }); 
+0

-1: Nie o to prosił. –

+0

To zwróci pary jako {{1,2}, {2,3}, ...} – Yakimych

+0

@Lasse To działa dobrze, ponieważ rozumiem pytanie, więc proszę odwołać swoje zgłoszenie –

-1

daje wszystkie możliwe pary (vb.net):

Dim nums() = {1, 2, 3, 4, 5, 6, 7} 
Dim pairs = From a In nums, b In nums Where a <> b Select a, b 

Edit:

Dim allpairs = From a In nums, b In nums Where b - a = 1 Select a, b 
Dim uniquePairs = From p In allpairs Where p.a Mod 2 <> 0 Select p 

uwaga: ostatnia para jest brakujący, pracujący nad nim

Edit:

unia uniquePairs z parą {nums.Last,0}

+0

Nie o to prosiłem. –

+0

Nie zauważyłem, że pary były w seuqence. redagowałem odpowiedź –

1

Może to być nieco bardziej ogólne niż potrzebują - można ustawić niestandardowy itemsInGroup:

int itemsInGroup = 2; 
var pairs = nums. 
      Select((n, i) => new { GroupNumber = i/itemsInGroup, Number = n }). 
      GroupBy(n => n.GroupNumber). 
      Select(g => g.Select(n => n.Number).ToList()). 
      ToList(); 

EDIT:

Jeśli chcesz dodać zera (lub inny numer) na wypadek, gdyby ostatnia grupa miała inny rozmiar:

int itemsInGroup = 2; 
int valueToAppend = 0; 
int numberOfItemsToAppend = itemsInGroup - nums.Count() % itemsInGroup; 

var pairs = nums. 
      Concat(Enumerable.Repeat(valueToAppend, numExtraItems)). 
      Select((n, i) => new { GroupNumber = i/itemsInGroup, Number = n }). 
      GroupBy(n => n.GroupNumber). 
      Select(g => g.Select(n => n.Number).ToList()). 
      ToList(); 
+0

Cool! Jedno zastrzeżenie, ostatni element w parach będzie miał tylko 1 element, jeśli lista liczb ma nieparzystą liczbę. Na przykład. nums = {1,2,3} => par = {{1, 2}, {3}} –

+0

Zobacz aktualizację dla ogólnego przypadku. Jeśli potrzebujesz tego tylko z dwiema rzeczami na grupę, możesz zrobić prostsze sprawdzenie 'nums.Count()' i dołączyć jedno '0', jeśli jest ono nieparzyste. – Yakimych

0
int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7 }; 
var result = numbers.Zip(numbers.Skip(1).Concat(new int[] { 0 }), (x, y) => new 
     { 
      First = x, 
      Second = y 
     }).Where((item, index) => index % 2 == 0); 
+0

Zawsze dodam element, którego wartość wynosi 0, zostanie automatycznie zignorowany, jeśli długość liczb jest parzysta. –

+0

Nie wiedziałem, że istnieje wersja Where(), która obsługuje indeksowanie. +1 –

+0

I działa również dla list o nieparzystej długości :) –

1

(ostrzeżenie: wygląda brzydki)

var pairs = x.Where((i, val) => i % 2 == 1) 
      .Zip(
      x.Where((i, val) => i % 2 == 0), 
       (first, second) => 
       new 
       { 
        First = first, 
        Second = second 
       }) 
      .Concat(x.Count() % 2 == 1 ? new[]{ 
       new 
       { 
        First = x.Last(), 
        Second = default(int) 
       }} : null); 
+0

Kradnąc z Danny Chena, możesz dodać element "0" do drugiego argumentu Zip i tym samym pozbyć się ostatniego bloku .Concat (...). –

0
var w = 
     from ei in nums.Select((e, i) => new { e, i }) 
     group ei.e by ei.i/2 into g 
     select new { f = g.First(), s = g.Skip(1).FirstOrDefault() }; 
7

Żadna z metod LINQ domyślnych można to zrobić leniwie iz jednym skanie. Zinstaluj sekwencję ze sobą, wykonując 2 skany, a zgrupowanie nie jest całkowicie leniwe. Najprościej jest wdrożenie go bezpośrednio:

public static IEnumerable<T[]> Partition<T>(this IEnumerable<T> sequence, int partitionSize) { 
    Contract.Requires(sequence != null) 
    Contract.Requires(partitionSize > 0) 

    var buffer = new T[partitionSize]; 
    var n = 0; 
    foreach (var item in sequence) { 
     buffer[n] = item; 
     n += 1; 
     if (n == partitionSize) { 
      yield return buffer; 
      buffer = new T[partitionSize]; 
      n = 0; 
     } 
    } 
    //partial leftovers 
    if (n > 0) yield return buffer; 
} 
+0

Patrząc na wszystkie odpowiedzi, wydaje się, że Linq nie jest tutaj najlepszym rozwiązaniem. Twoja implementacja jest całkiem czysta. –

+0

Powinien to być 'public static IEnumerable Partition ...'. –

1
public static IEnumerable<List<T>> InSetsOf<T>(this IEnumerable<T> source, int max) 
{ 
    return InSetsOf(source, max, false, default(T)); 
} 

public static IEnumerable<List<T>> InSetsOf<T>(this IEnumerable<T> source, int max, bool fill, T fillValue) 
{ 
    var toReturn = new List<T>(max); 
    foreach (var item in source) 
    { 
     toReturn.Add(item); 
     if (toReturn.Count == max) 
     { 
      yield return toReturn; 
      toReturn = new List<T>(max); 
     } 
    } 
    if (toReturn.Any()) 
    { 
     if (fill) 
     { 
      toReturn.AddRange(Enumerable.Repeat(fillValue, max-toReturn.Count)); 
     } 
     yield return toReturn; 
    } 
} 

Wykorzystanie:

var pairs = nums.InSetsOf(2, true, 0).ToArray(); 
0
IList<int> numbers = new List<int> {1, 2, 3, 4, 5, 6, 7}; 
var grouped = numbers.GroupBy(num => 
{ 
    if (numbers.IndexOf(num) % 2 == 0) 
    { 
     return numbers.IndexOf(num) + 1; 
    } 
    return numbers.IndexOf(num); 
}); 

Jeśli potrzebujesz ostatnią parę wypełnione zera można po prostu dodać go przed wykonaniem grupowanie if lista jest nieparzysta.

if (numbers.Count() % 2 == 1) 
{ 
    numbers.Add(0); 
} 

Innym rozwiązaniem mogłoby być:

var groupedIt = numbers 
    .Zip(numbers.Skip(1).Concat(new[]{0}), Tuple.Create) 
    .Where((x,i) => i % 2 == 0); 

Albo użyć MoreLinq że ma wiele przydatnych rozszerzeń:

IList<int> numbers = new List<int> {1, 2, 3, 4, 5, 6, 7}; 
var batched = numbers.Batch(2);