LINQ - merge 2 lists but with specific order - c#

I want to merge 2 lists into one list, however, it have to be in special order.
For example, I have a list of type A:
{ A, A ,A ,A ,A, A .... }
And also a list of type B:
{B, B, B , B ....}
And desired result should be like this:
{A, A, B, A, A, B, A, A, B}
The merge should take the 2 items from list A, and then 1 item from List B.
One thing to note is, in case of one list gets empty, fill all the rest items with the second list's items.
I trying to find an elegant way to do it with LINQ.
Heres my code, but it's a bit long and I hope there is a better way to do it via linq:
Thanks a lot.
public IList<PersonBase> Order(IList<Person1> people1, IList<Person2> people2)
{
if (people1.IsNullOrEmpty())
return people2;
if (people2.IsNullOrEmpty())
return people1;
List<PersonBase> orderedList = new List<PersonBase>();
var people1Count = 0;
var people2Count = 0;
while (people2Count < people2.Count || people1Count < people1.Count)
{
var people1ToAdd = tags.Skip(people1Count).Take(1).ToList();
people1Count = people1.Count();
orderedList.AddRange(people1ToAdd);
if (people1Count >= people1.Count)
{
orderedList.AddRange(people2.Skip(people2Count));
break;
}
var people2ToAdd = people2.Skip(peopleCount).Take(2).ToList();
people2Count = people2.Count();
orderedList.AddRange(people2ToAdd);
if (people2Count >= people2.Count)
{
orderedList.AddRange(people1.Skip(people1Count));
break;
}
}
return orderedList;
}

This is pretty horrible code but does what you want it too. Basically we keep track of each lists index and have an int that keeps track of which list to use to populate the result array.
List<int> list1 = new List<int>() { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
List<int> list2 = new List<int>() { 2, 2, 2, 2, 2 };
int list1Counter = 0;
int list2Counter = 0;
int arraychecker = 1;
int[] resultArray = new int[list1.Count + list2.Count];
for (int i = 0; i < resultArray.Length; i++)
{
if (list1Counter < list1.Count && list2Counter < list2.Count)
{
if (arraychecker == 1 || arraychecker == 2)
{
resultArray[i] = list1[list1Counter];
list1Counter++;
arraychecker++;
}
else
{
resultArray[i] = list2[list2Counter];
list2Counter++;
arraychecker = 1;
}
}
else if (list1Counter < list1.Count)
{
resultArray[i] = list1[list1Counter];
list1Counter++;
}
else
{
resultArray[i] = list2[list2Counter];
list2Counter++;
}
}

You can calculate the index for each item and then order by that index.
var mergedList =
listA.Select((item, index) =>
new { Index = index / 2 * 3 + (i % 2), Item = item})
.Concat(listB.Select((item, index) =>
new { Index = i * 3 + 2, Item = item}))
.OrderBy(x => x.Index)
.Select(x => x.Item)
.ToList();
Or write a method. This is more efficient, since it doesn't need to sort; It just runs through each list once.
static IEnumerable<T> Alternate<T>(IEnumerable<T> sourceA, IEnumerable<T> sourceB) {
using (IEnumerator<T> eA = sourceA.GetEnumerator(), eB = sourceB.GetEnumerator()) {
bool aHasItems = true, bHasItems = true;
while (aHasItems || bHasItems) {
if (eA.MoveNext()) yield return eA.Current;
if (aHasItems = eA.MoveNext()) yield return eA.Current;
if (bHasItems = eB.MoveNext()) yield return eB.Current;
}
}
}

This is the best I could do:
string[] test1 = { "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A" };
string[] test2 = { "B", "B" };
string[] result = test2.SelectMany((value, key) => test1.Skip(key * 2).Take(2).Concat(test2.Skip(key).Take(1))).ToArray();
result = result.Concat(test1.Skip(result.Length / 3 * 2).Take(test1.Length - result.Length / 3 * 1)).ToArray();
This will take 2 from array1 then 1 from array 2 then add whats left of the longer one. Output:
AABAABAAAAAAAAA

Related

Linq splitting group if there is missing number

This one has totally stumped me.
Let's say we have a list of integers
var list = new List {
1,
2,
3,
5,
6,
7,
9,
10
};
How can I group this where this would be 1-3,5-7,9-10 the group is split where the next integer is missing?
See if this works. No for loop, just linq
List<int> list = new List<int> { 1, 2, 3, 5, 6, 7, 9, 10};
List<int> splitIndex = list.Skip(1).Select((x,i) => new { x = x, i = i}).Where(x => list[x.i] + 1 != x.x).Select(x => x.i).ToList();
//add last index
splitIndex.Add(list.Count - 1);
var results = splitIndex.Select((x,i) => (i == 0) ? list.Take(x + 1).ToList() : list.Skip(splitIndex[i - 1] + 1).Take(splitIndex[i] - splitIndex[i - 1]).ToList()).ToList();
You won't achieve it with simple LINQ, but you can write your own extension method that can deal with such grouping.
You have to place it in static class, and call it like normal LINQ.
public static class LinqExtensions
{
public static IEnumerable<IEnumerable<int>> GroupSequential (
this IEnumerable<int> source)
{
var previous = source.First();
var list = new List<int>() { previous };
foreach (var item in source.Skip(1))
{
if (item - previous != 1)
{
yield return list;
list = new List<int>();
}
list.Add(item);
previous = item;
}
yield return list;
}
}
and call it like list.GroupSequential()
I think this should work for your needs.
I agree with #Arion that it probably isn't possible with a readable LINQ method chain. #jdweng proved me wrong though :-)
I'd like to offer my alternative solution. It's an extension method, and it utilizes a custom Interval type.
Range:
public struct Interval
{
public Interval(int from, int to)
{
From = from;
To = to;
}
public int From { get; }
public int To { get; }
public IEnumerable<int> Members() => Enumerable.Range(From, To - From + 1);
}
To get the numbers within the Range, you would use Numbers() function. Numbers are lazily generated, thus saving space unless you need them all.
The extension:
public static class EnumerableExtensions
{
public static IEnumerable<Interval> GetIntervals(this IEnumerable<int> numbers)
{
var array = numbers.OrderBy(x => x).ToArray();
var fromIndex = 0;
var toIndex = fromIndex;
for (var i = 1; i < array.Length; i++)
{
var current = array[i];
if (current == array[toIndex] + 1)
{
toIndex++;
}
else if (fromIndex != toIndex)
{
yield return new Interval(array[fromIndex], array[toIndex]);
fromIndex = i;
toIndex = fromIndex;
}
}
if (toIndex != fromIndex)
{
yield return new Interval(array[fromIndex], array[toIndex]);
}
}
}
The usage:
public void Demo()
{
var list = new List<int> {1, 2, 3, 5, 6, 7, 9, 10};
// 1-3, 5-7, 9-10, lazily generated
var intervals = list.GetIntervals();
foreach (var interval in intervals)
{
// [1, 2, 3], then [5, 6, 7], then [9, 10], lazily generated
var members = interval.Members();
foreach (var numberInRange in members)
{
// do something with numberInRange
}
}
}
Can be simplified a bit :
var list = new List<int> { 1,2,3, 5,6,7, 9,10 };
List<List<int>> result = list.Aggregate(new List<List<int>>(), (L, n) => {
if (L.Count < 1 || L.Last().Last() < n - 1) L.Add(new List<int>());
L.Last().Add(n);
return L;
});
Its unclear what format you want it back as, but this extension will report the missing numbers in a list and return a list of the missing numbers:
public static IEnumerable<int> SequenceFindMissings(this IList<int> sequence)
{
var missing = new List<int>();
if ((sequence != null) && (sequence.Any()))
{
sequence.Aggregate((seed, aggr) =>
{
var diff = (aggr - seed) - 1;
if (diff > 0)
missing.AddRange(Enumerable.Range((aggr - diff), diff));
return aggr;
});
}
return missing;
}
Usage
var list = new List<int> {1,2,3,5,6,7,9,10};
var missings = list.SequenceFindMissings(); // { 4, 8 }
Its a topic covered on my blog:
C# Linq: Find Missing Values in a Sequence of Numbers and Other Sequence Related lists as IEnumerable Extensions
I found this LINQ solution more easy to understand and debug than the accepted one IMHO:
var list = new List<int>() { 1, 2, 3, 5, 6, 7, 9, 10 };
int groupIndex = 0;
int previousNumber = list.First();
var groups = list.Select((n, i) =>
{
previousNumber = (i == 0) ? list[i] : list[i - 1];
return new
{
Number = n,
GroupIndex = (i == 0) || (previousNumber + 1 == list[i]) ? groupIndex : ++groupIndex
};
}).GroupBy(x => x.GroupIndex).Select(g => g.Select(x => x.Number).ToList()).ToList();
If you want to go crazy, you can try the below 1 linq,
var desiredOutput = string.Join(',', string.Join(',', Enumerable.Range(list.First(), list.Last())).Split(Enumerable.Range(list.First(), list.Last())
.Except(list).Select(y => y.ToString()).ToArray(), StringSplitOptions.None)
.Select(x => string.Concat(x.Trim(',').Split(',').First(), "-", x.Trim(',').Split(',').Last())));
Or for more clear format, you can try the below
var sequenceOrder = Enumerable.Range(list.First(), list.Last()).ToList();
var splitters = sequenceOrder.Except(list).Select(y => y.ToString()).ToArray();
var joinedSequence = string.Join(',', sequenceOrder);
var result = joinedSequence.Split(splitters, StringSplitOptions.None).Select(x => string.Concat(x.Trim(',').Split(',').First(), "-", x.Trim(',').Split(',').Last()));
var desiredOutput = string.Join(',', result);

Combine entries from two lists by position using LINQ

Say I have two lists with following entries
List<int> a = new List<int> { 1, 2, 5, 10 };
List<int> b = new List<int> { 6, 20, 3 };
I want to create another List c where its entries are items inserted by position from two lists. So List c would contain the following entries:
List<int> c = {1, 6, 2, 20, 5, 3, 10}
Is there a way to do it in .NET using LINQ? I was looking at .Zip() LINQ extension, but wasn't sure how to use it in this case.
Thanks in advance!
To do it using LINQ, you can use this piece of LINQPad example code:
void Main()
{
List<int> a = new List<int> { 1, 2, 5, 10 };
List<int> b = new List<int> { 6, 20, 3 };
var result = Enumerable.Zip(a, b, (aElement, bElement) => new[] { aElement, bElement })
.SelectMany(ab => ab)
.Concat(a.Skip(Math.Min(a.Count, b.Count)))
.Concat(b.Skip(Math.Min(a.Count, b.Count)));
result.Dump();
}
Output:
This will:
Zip the two lists together (which will stop when either runs out of elements)
Producing an array containing the two elements (one from a, another from b)
Using SelectMany to "flatten" this out to one sequence of values
Concatenate in the remainder from either list (only one or neither of the two calls to Concat should add any elements)
Now, having said that, personally I would've used this:
public static IEnumerable<T> Intertwine<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
using (var enumerator1 = a.GetEnumerator())
using (var enumerator2 = b.GetEnumerator())
{
bool more1 = enumerator1.MoveNext();
bool more2 = enumerator2.MoveNext();
while (more1 && more2)
{
yield return enumerator1.Current;
yield return enumerator2.Current;
more1 = enumerator1.MoveNext();
more2 = enumerator2.MoveNext();
}
while (more1)
{
yield return enumerator1.Current;
more1 = enumerator1.MoveNext();
}
while (more2)
{
yield return enumerator2.Current;
more2 = enumerator2.MoveNext();
}
}
}
Reasons:
It doesn't enumerate a nor b more than once
I'm skeptical about the performance of Skip
It can work with any IEnumerable<T> and not just List<T>
I'd create an extension method to do it.
public static List<T> MergeAll<T>(this List<T> first, List<T> second)
{
int maxCount = (first.Count > second. Count) ? first.Count : second.Count;
var ret = new List<T>();
for (int i = 0; i < maxCount; i++)
{
if (first.Count < maxCount)
ret.Add(first[i]);
if (second.Count < maxCount)
ret.Add(second[i]);
}
return ret;
}
This would iterate through both lists once. If one list is bigger than the other it will continue to add until it's done.
You could try this code:
List<int> c = a.Select((i, index) => new Tuple<int, int>(i, index * 2))
.Union(b.Select((i, index) => new Tuple<int, int>(i, index * 2 + 1)))
.OrderBy(t => t.Second)
.Select(t => t.First).ToList();
It makes a union of two collections and then sorts that union using index. Elements from the first collection have even indices, from the second - odd ones.
Just wrote a little extension for this:
public static class MyEnumerable
{
public static IEnumerable<T> Smash<T>(this IEnumerable<T> one, IEnumerable<T> two)
{
using (IEnumerator<T> enumeratorOne = one.GetEnumerator(),
enumeratorTwo = two.GetEnumerator())
{
bool twoFinished = false;
while (enumeratorOne.MoveNext())
{
yield return enumeratorOne.Current;
if (!twoFinished && enumeratorTwo.MoveNext())
{
yield return enumeratorTwo.Current;
}
}
if (!twoFinished)
{
while (enumeratorTwo.MoveNext())
{
yield return enumeratorTwo.Current;
}
}
}
}
}
Usage:
var a = new List<int> { 1, 2, 5, 10 };
var b = new List<int> { 6, 20, 3 };
var c = a.Smash(b); // 1, 6, 2, 20, 5, 3, 10
var d = b.Smash(a); // 6, 1, 20, 2, 3, 5, 10
This will work for any IEnumerable so you can also do:
var a = new List<string> { "the", "brown", "jumped", "the", "lazy", "dog" };
var b = new List<string> { "quick", "dog", "over" };
var c = a.Smash(b); // the, quick, brown, fox, jumped, over, the, lazy, dog
You could use Concat and an anonymous type which you order by the index:
List<int> c = a
.Select((val, index) => new { val, index })
.Concat(b.Select((val, index) => new { val, index }))
.OrderBy(x => x.index)
.Select(x => x.val)
.ToList();
However, since that's not really elegant and also less efficient than:
c = new List<int>(a.Count + b.Count);
int max = Math.Max(a.Count, b.Count);
int aMax = a.Count;
int bMax = b.Count;
for (int i = 0; i < max; i++)
{
if(i < aMax)
c.Add(a[i]);
if(i < bMax)
c.Add(b[i]);
}
I wouldn't use LINQ at all.
Sorry for adding a third extension method inspired by the other two, but I like it shorter:
static IEnumerable<T> Intertwine<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
using (var enumerator1 = a.GetEnumerator())
using (var enumerator2 = b.GetEnumerator()) {
bool more1 = true, more2 = true;
do {
if (more1 && (more1 = enumerator1.MoveNext()))
yield return enumerator1.Current;
if (more2 && (more2 = enumerator2.MoveNext()))
yield return enumerator2.Current;
} while (more1 || more2);
}
}

unequal size lists to merge

I have searched without success to a similar situation as follows.
I have two lists, list A and list B.
List A is composed of 10 objects created from ClassA which contains only strings.
List B is composed of 100 objects created from ClassB which only contains decimals.
List A is the header information.
List B is the data information.
The relationship between the two lists is:
Row 1 of list A corresponds to rows 1-10 of list B.
Row 2 of list A corresponds to rows 11-20 of list B.
Row 3 of list A corresponds to rows 21-30 of list B.
etc.........
How can I combine these two lists so that when I display them on the console the user will see a header row followed immediately by the corresponding 10 data rows.
I apologize if this has been answered before.
Ok, that should work. Let me know in case I got anything wrong.
List<ClassA> listA = GetListA()// ...
List<ClassB> listB = GetListA()// ...
if(listB.Count % listA.Count != 0)
throw new Exception("Unable to match listA to listB");
var datasPerHeader = listB.Count / listA.Count;
for(int i = 0; i < listA.Count;i++)
{
ClassA header = listA[i];
IEnumerable<ListB> datas = listB.Skip(datasPerHeader*i).Take(datasPerHeader);
Console.WriteLine(header.ToString());
foreach(var data in datas)
{
Console.WriteLine("\t{0}", data.ToString());
}
}
Here is some code that should fulfill your request - I am going to find a link for the partition extension as I can't find it in my code anymore:
void Main()
{
List<string> strings = Enumerable.Range(1,10).Select(x=>x.ToString()).ToList();
List<decimal> decimals = Enumerable.Range(1,100).Select(x=>(Decimal)x).ToList();
var detailsRows = decimals.Partition(10)
.Select((details, row) => new {HeaderRow = row, DetailsRows = details});
var headerRows = strings.Select((header, row) => new {HeaderRow = row, Header = header});
var final = headerRows.Join(detailsRows, x=>x.HeaderRow, x=>x.HeaderRow, (header, details) => new {Header = header.Header, Details = details.DetailsRows});
}
public static class Extensions
{
public static IEnumerable<List<T>> Partition<T>(this IEnumerable<T> source, Int32 size)
{
for (int i = 0; i < Math.Ceiling(source.Count() / (Double)size); i++)
yield return new List<T>(source.Skip(size * i).Take(size));
}
}
That Partition method is the one that does the grunt work...
And here is the link to the article - LINK
EDIT 2
Here is better code for the Main() method... Rushed to answer and forgot brain:
void Main()
{
List<string> strings = Enumerable.Range(1,10).Select(x=>x.ToString()).ToList();
List<decimal> decimals = Enumerable.Range(1,100).Select(x=>(Decimal)x).ToList();
var detailsRows = decimals.Partition(10);
var headerRows = strings; //just renamed for clarity from other code
var final = headerRows.Zip(detailsRows, (header, details) => new {Header = header, Details = details});
}
This should be pretty straight forward unless I'm missing something.
var grouped = ListA.Select((value, index) =>
new {
ListAItem = value,
ListBItems = ListB.Skip(index * 10).Take(10)
})
.ToList();
Returns back an anonymous type you can loop through.
foreach (var group in grouped)
{
Console.WriteLine("List A: {0}", group.Name);
foreach (var listBItem in group.ListBItems)
{
Console.WriteLine("List B: {0}", listBItem.Name);
{
}
The easiest way may be something like this:
var listA = new List<string>() { "A", "B", "C", ... }
var listB = new List<decimal>() { 1m, 2m, 3m, ... }
double ratio = ((double)listA.Count) / listB.Count;
var results =
from i in Enumerable.Range(0, listB.Count)
select new { A = listA[(int)Math.Truncate(i * ratio)], B = listB[i] };
Or in fluent syntax:
double ratio = ((double)listA.Count) / listB.Count;
var results = Enumerable.Range(0, listB.Count)
.Select(i => new { A = listA[(int)Math.Truncate(i * ratio)], B = listB[i] });
Of course if you know you will always have 10 items in listB for each item in listA, you can simplify this to:
var results =
from i in Enumerable.Range(0, listB.Count)
select new { A = listA[i / 10], B = listB[i] };
Or in fluent syntax:
var results = Enumerable.Range(0, listB.Count)
.Select(i => new { A = listA[i / 10], B = listB[i] });
This will return a result set like
{ { "A", 1 },
{ "A", 2 },
{ "A", 3 }
..,
{ "A", 10 },
{ "B", 11 },
{ "B", 12 },
{ "B", 13 },
...
{ "B", 20 },
{ "C", 21 },
...
{ "J", 100 }
}

Group a list into groups of 3 and select max of each group

I have a list of lists of dynamic which is currently being filtered through this:
var CPUdataIWant = from s in rawData
where s.stat.Contains("CPU")
select s;
//CPUDataIWant is a List<List<dynamic>>.
I have 86000 values in each inner list.
And what I need to do, is group the values into groups of 3, select the max of that group, and insert that into another list of List of dynamic, or just filter it out of CPUDataIWant.
So an example of what I want would be:
Raw data = 14,5,7,123,5,1,43,87,9
And my processed value would be:
ProceData = [14,5,7], [123,5,1], [43,87,9]
ProceData = [14,123,87]
Doesn't have to be using linq but the easier the better.
EDIT: Ok I explained what a wanted a bit poorly.
here's what I have:
List<List<object>>
In this List, I'll have X amount of Lists called A.
In A I'll have 86000 values, let's say they're ints for now.
What I'd like, is to have
List<List<object>>
But instead of 86000 values in A, I want 28700, which would be made from the max of every 3 values in A.
IEnumerable<int> filtered = raw.Select((x, i) => new { Index = i, Value = x }).
GroupBy(x => x.Index / 3).
Select(x => x.Max(v => v.Value));
or, if you plan to use it more often
public static IEnumerable<int> SelectMaxOfEvery(this IEnumerable<int> source, int n)
{
int i = 0;
int currentMax = 0;
foreach (int d in source)
{
if (i++ == 0)
currentMax = d;
else
currentMax = Math.Max(d, currentMax);
if (i == n)
{
i = 0;
yield return currentMax;
}
}
if (i > 0)
yield return currentMax;
}
//...
IEnumerable<int> filtered = raw.SelectMaxOfEvery(3);
Old-school way of doing things makes it quite simple (although it's not as compact as LINQ):
// Based on this spec: "CPUDataIWant is a List<List<dynamic>>"
// and on the example, which states that the contents are numbers.
//
List<List<dynamic>> filteredList = new List<List<dynamic>>();
foreach (List<dynamic> innerList in CPUDataIWant)
{
List<dynamic> innerFiltered = new List<dynamic>();
// if elements are not in multiples of 3, the last one or two won't be checked.
for (int i = 0; i < innerList.Count; i += 3)
{
if(innerList[i+1] > innerList[i])
if(innerList[i+2] > innerList[i+1])
innerFiltered.Add(innerList[i+2]);
else
innerFiltered.Add(innerList[i+1]);
else
innerFiltered.Add(innerList[i]);
}
filteredList.Add(innerFiltered);
}
This should give the desired result:
var data = new List<dynamic> { 1, 2, 3, 3, 10, 1, 5, 2, 8 };
var firsts = data.Where((x, i) => i % 3 == 0);
var seconds = data.Where((x, i) => (i + 2) % 3 == 0);
var thirds = data.Where((x, i) => (i + 1) % 3 == 0);
var list = firsts.Zip(
seconds.Zip(
thirds, (x, y) => Math.Max(x, y)
),
(x, y) => Math.Max(x, y)
).ToList();
List now contains:
3, 10, 8
Or generalized to an extension method:
public static IEnumerable<T> ReduceN<T>(this IEnumerable<T> values, Func<T, T, T> map, int N)
{
int counter = 0;
T previous = default(T);
foreach (T item in values)
{
counter++;
if (counter == 1)
{
previous = item;
}
else if (counter == N)
{
yield return map(previous, item);
counter = 0;
}
else
{
previous = map(previous, item);
}
}
if (counter != 0)
{
yield return previous;
}
}
Used like this:
data.ReduceN(Math.Max, 3).ToList()
If you felt a need to use Aggregate you could do it like this:
(tested wiht LinqPad)
class Holder
{
public dynamic max = null;
public int count = 0;
}
void Main()
{
var data = new List<dynamic>
{new { x = 1 }, new { x = 2 }, new { x = 3 },
new { x = 3 }, new { x = 10}, new { x = 1 },
new { x = 5 }, new { x = 2 }, new { x = 1 },
new { x = 1 }, new { x = 9 }, new { x = 3 },
new { x = 11}, new { x = 10}, new { x = 1 },
new { x = 5 }, new { x = 2 }, new { x = 12 }};
var x = data.Aggregate(
new LinkedList<Holder>(),
(holdList,inItem) =>
{
if ((holdList.Last == null) || (holdList.Last.Value.count == 3))
{
holdList.AddLast(new Holder { max = inItem, count = 1});
}
else
{
if (holdList.Last.Value.max.x < inItem.x)
holdList.Last.Value.max = inItem;
holdList.Last.Value.count++;
}
return holdList;
},
(holdList) => { return holdList.Select((h) => h.max );} );
x.Dump("We expect 3,10,5,9,11,12");
}

Linq to Objects - return pairs of numbers from list of numbers

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} }
The elements of pairs should be either two-element lists, or instances of some anonymous class with two fields, something like new {First = 1, Second = 2}.
None of the default linq methods can do this lazily and with a single scan. Zipping the sequence with itself does 2 scans and grouping is not entirely lazy. Your best bet is to implement it directly:
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;
}
Try this:
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});
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);
(warning: looks ugly)
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);
This might be a bit more general than you require - you can set a custom 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:
If you want to append zeros (or some other number) in case the last group is of a different size:
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();
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;
}
}
usage:
var pairs = nums.InSetsOf(2, true, 0).ToArray();
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);
});
If you need the last pair filled with zero you could just add it before doing the grouping if the listcount is odd.
if (numbers.Count() % 2 == 1)
{
numbers.Add(0);
}
Another approach could be:
var groupedIt = numbers
.Zip(numbers.Skip(1).Concat(new[]{0}), Tuple.Create)
.Where((x,i) => i % 2 == 0);
Or you use MoreLinq that has a lot of useful extensions:
IList<int> numbers = new List<int> {1, 2, 3, 4, 5, 6, 7};
var batched = numbers.Batch(2);
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() };
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] });
Another option is to use the SelectMany LINQ method. This is more for those who wish to iterate through a list of items and for each item return 2 or more of it's properties. No need to loop through the list again for each property, just once.
var list = new [] {//Some list of objects with multiple properties};
//Select as many properties from each Item as required.
IEnumerable<string> flatList = list.SelectMany(i=> new[]{i.NameA,i.NameB,i.NameC});
Another simple solution using index and index + 1.
var nums = Enumerable.Range(1, 10);
var pairs = nums.Select((item, index) =>
new { First = item, Second = nums.ElementAtOrDefault(index + 1) })
.SkipLastN(1);
pairs.ToList().ForEach(p => Console.WriteLine($"({p.First}, {p.Second}) "));
Last item is invalid and must be removed with SkipLastN().
this gives all possible pairs(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
note: the last pair is missing, working on it
Edit:
union uniquePairs with the pair {nums.Last,0}

Categories

Resources