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

=> pairs = {{1, 2}, {3, 4}, {5, 6}, {7, 0}}Linq zu Objekte - Rückkehr Zahlenpaare aus der Liste der Zahlen

Die Elemente von pairs sollten entweder zweielementige Listen oder Instanzen einer anonymen Klasse mit zwei Feldern sein, etwa new {First = 1, Second = 2}.

+1

Genaue Duplikat Frage selbst gefragt http: // Stackoverflow.com/questions/3575925/linq-to-return-all-pair-of-elements-from-two-lists –

+0

@Jani Nein, ist es nicht. Das verlangt nach einer Entsprechung zu Pythons (oder Rubys) Zip() Methode -> nimmt zwei Listen und erstellt eine Liste von Tupeln. Diese Frage betrifft die Partitionierung einer einzelnen Liste. –

+2

@Cristi bekam es, sorry –

Antwort

3

Versuchen Sie folgendes:

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

bis Sie .Select tun können ((n, i) => ...) einen automatischen Zähler –

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 zu erhalten: Das ist nicht das, was er verlangte. –

+0

dass Paare kehrt als {{1,2}, {2,3}, ...} – Yakimych

+0

@Lasse Es funktioniert gut, wie ich die Frage so verstehen Sie bitte Ihre downvote –

-1

dies gibt alle möglichen Paare (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 

Anmerkung: die letzte Paar ist fehlt, arbeitet daran

Bearbeiten

:

Vereinigung uniquePairs mit dem Paar {nums.Last,0}

+0

widerrufen, das nicht das, was für, fragte ich. –

+0

Ich habe nicht bemerkt, dass die Paare in der Nähe waren. Ich habe die Antwort bearbeitet –

1

Dies könnte ein wenig allgemeiner sein, als Sie benötigen - Sie eine benutzerdefinierte einstellen 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:

Wenn Sie Nullen (oder eine andere Zahl) anhängen möchten, falls die letzte Gruppe eine andere Größe hat:

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! Eine Einschränkung, das letzte Element in Paaren wird nur 1 Element haben, wenn die Liste der Zahlen ungerade zählt. Z.B. nums = {1,2,3} => Paare = {{1, 2}, {3}} –

+0

Siehe Update für den allgemeinen Fall. Wenn Sie dies nur mit zwei Elementen pro Gruppe benötigen, können Sie eine einfachere Überprüfung für 'nums.Count()' durchführen und eine '0' anhängen, wenn sie ungerade ist. – 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

Ich werde immer ein Element hinzufügen, dessen Wert 0 ist, es wird automatisch ignoriert, wenn die Länge der Zahlen gerade ist. –

+0

Ich wusste nicht, dass es eine Where() - Version gibt, die Indexierung unterstützt. +1 –

+0

Und es funktioniert für Listen von ungerader Länge zu :) –

1

(Warnung: sieht hässlich)

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

Stehlen von Danny Chen, könnten Sie eine ‚0‘ Element mit dem zweiten Argument von Zip anhängen und damit der letzten .Concat (...) Block loszuwerden. –

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

Keine der Standard Linq Methoden diese lazily tun können, und mit einem einzigen Scan. Zippen der Sequenz mit sich selbst macht 2 Scans und Gruppierung ist nicht ganz faul. Ihre beste Wette ist es direkt zu implementieren:

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

Wenn man alle Antworten betrachtet, scheint Linq nicht der beste Ansatz zu sein. Ihre Implementierung ist ziemlich sauber. –

+0

Es sollte "öffentliche statische IEnumerable Partition ..." sein. –

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; 
    } 
} 

Nutzung:

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); 
}); 

Wenn Sie das letzte Paar gefüllt müssen mit Null könnte man es nur hinzufügen, bevor die Gruppierung zu tun, wenn Der Listenzähler ist ungerade.

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

konnte Ein anderer Ansatz sein:

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

Oder Sie verwenden MoreLinq, die viele nützliche Erweiterungen hat:

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