I am working in C#.
How to combine (sum, plus, minus) these class elements in both lists?
class Attribute
{
public AttributeType WhatAttri;
public float amount;
}
enum AttributeType{
maxhp, str, dex, int, wis,,,,
}
Attribute[] attList1;
Attribute[] attList2;
If specific values are like this,
attList1[0] = new Attribute(AttributeType.maxhp, 6)
attList1[1] = new Attribute(AttributeType.str, 4)
attList1[2] = new Attribute(AttributeType.dex, 3)
attList2[0] = new Attribute(AttributeType.str, 9)
attList2[1] = new Attribute(AttributeType.int, 7)
attList2[2] = new Attribute(AttributeType.wis, 5)
I want final result like this, (attList1 values are added, attList2 values are deducted, and also sum(or minus or plus) duplicated AttributeType)
So at above two lists, AttributeType.str is same, so deduct duplicated attList2[0]'s amount variable's value (9) from attList1[1]'s value (4)
and exclude this element from attList2.
So final result should be,
Attribute[] combinedList; (or List<Attribute> combinedList )
combinedList[0] = new Attribute(AttributeType.maxhp, 6)
combinedList[1] = new Attribute(AttributeType.str, -5) (4 - 9)
combinedList[2] = new Attribute(AttributeType.dex, 3)
combinedList[3] = new Attribute(AttributeType.int, -7)
combinedList[4] = new Attribute(AttributeType.wis, -5)
How to achieve this?
Thanks.
var result =
attList2.Select(a => new Attribute(a.WhatAttri, -a.amount)) // line 1
.Concat(attList1) // line 2
.GroupBy(a => a.WhatAttri) // line 3
.Select(g => new Attribute(g.Key, g.Sum(a => a.amount))); // line4
foreach(var a in result)
{
Console.WriteLine($"{a.WhatAttri}: {a.amount}");
}
You want to sum up the counts of the first list and subtract the amounts of the second list. So first I transform the second list to a new list with negative amounts (line 1). then the two lists are joined into one list (line 2).
Then the big line is grouped by type (line 3). and then you have a structure of Key and items, where you create new Attributes by using the key and the sum of the amounts (line 4).
Edit: replaced "Union" in line 2 by "Concat" to avoid dropping duplicate values in case there would be a custom comparer method in class Attribute
The main issue is that you didn't have positive or negative value in the attributes modifier. How whould you boost those attribute? Once it's fix the solution is easy
Add both list with concat, GroupBy AttributeType, and select the values.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var attributes = new Attribute[] {
new Attribute{WhatAttri=AttributeType.maxhp, amount=6 },
new Attribute{WhatAttri=AttributeType.str, amount=4 },
new Attribute{WhatAttri=AttributeType.dex, amount=3 },
};
//Attribute modifier has to be either positive or negative
var attributesModifier = new Attribute[] {
new Attribute{WhatAttri=AttributeType.str, amount=-9 },
new Attribute{WhatAttri=AttributeType.#int, amount=-7 },
new Attribute{WhatAttri=AttributeType.wis, amount=-5 },
};
var newAttributes = attributes
.Concat(attributesModifier)
.GroupBy(x => x.WhatAttri)
.Select(group =>
new Attribute {
WhatAttri = group.Key,
amount = group.Sum(g => g.amount)
});
newAttributes.Dump();
}
public class Attribute
{
public AttributeType WhatAttri { get; set; }
public float amount { get; set; }
}
public enum AttributeType
{
maxhp, str, dex, #int, wis
}
}
OnLine demo https://dotnetfiddle.net/3C7n7F
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public enum AttributeType
{
maxhp, str, dex, intel, wis,
}
[System.Serializable]
public class Attribute
{
public AttributeType WhatAttri;
public float amount;
public Attribute(AttributeType type, int a)
{
WhatAttri = type;
amount = a;
}
}
public class LinqTest : MonoBehaviour
{
Attribute[] attList1 = new Attribute[3];
Attribute[] attList2 = new Attribute[3];
void Start()
{
attList1[0] = new Attribute(AttributeType.maxhp, 6);
attList1[1] = new Attribute(AttributeType.str, 4);
attList1[2] = new Attribute(AttributeType.dex, 3);
attList2[0] = new Attribute(AttributeType.str, 9);
attList2[1] = new Attribute(AttributeType.intel, 7);
attList2[2] = new Attribute(AttributeType.wis, 5);
Calcul();
}
void Calcul()
{
var result = attList2
.Select(a => new Attribute(a.WhatAttri, -(int)a.amount)) // line 1
.Union(attList1) // line 2
.GroupBy(a => a.WhatAttri) // line 3
.Select(g => new Attribute(g.Key, g.Sum(a => (int)a.amount))); // line4
foreach (var a in result)
{
Debug.Log($"{a.WhatAttri}: {a.amount}");
}
}
}
This is final result of testing above code by answers.
Using Unity engine.
Related
I have an array list of List<string> that contains values in the following order ["1m", "1cm", "4km","2cm"] (Centimeters, meters and kilometers)
When I want to sort this array, I get a wrong answer. I use OrderBy:
List<string> data = new List<string> { "1m", "1cm", "4km","2cm" };
var result= data.OrderBy(x => x).ToList();
the result is:
{ "1cm", "1m", "2cm", "4km"}
But I want the answer to be this order-: { "1cm", "2cm", "1m", "4km"}
You have sorted the data alphabetically. First the first character is compared. Then the second character and...
You need to normalize the data based on cm(or m) and then sort.
List<string> data = new List<string> { "1m", "1cm", "4km","2cm" };
var result = data.OrderBy(x => lenghtCM(x));
public int lenghtCM(string lenghtStr)
{
if (lenghtStr.Contains("cm"))
{
string num = lenghtStr.Split("cm")[0];
return int.Parse(num);
}
else if (lenghtStr.Contains("km"))
{
string num = lenghtStr.Split("km")[0];
return int.Parse(num) * 100*1000;
}
else if (lenghtStr.Contains("m"))
{
string num = lenghtStr.Split('m')[0];
return int.Parse(num) * 100;
}
return 0;
}
then the result:
{ "1cm", "2cm", "1m", "4km"}
private string[] normalaizeArray(string[] inputArray)
{
for (int i= 0 ; i < inputArray.Length; i++)
{
if(inputArray[i].Contains('m'))
{
inputArray[i] = (float.Parse(inputArray[i].Split('k')[0]) * 100).ToString();
} else if(inputArray[i].Contains('km'))
{
inputArray[i] = (float.Parse(inputArray[i].Split('k')[0]) * 100*1000).ToString();
}
else
{
inputArray[i] = inputArray[i].Replace("cm", "");
}
}
inputArray = inputArray.OrderBy(x => int.Parse(x)).ToArray();
for (int i = 0; i < inputArray.Length; i++)
{
if(int.Parse(inputArray[i])>1000*100)
inputArray[i] = (float.Parse(inputArray[i])/1000).ToString() + "km";
else if(int.Parse(inputArray[i])>100)
inputArray[i] = (float.Parse(inputArray[i])/100).ToString() + "m";
else
inputArray[i] = inputArray[i] + 'cm';
}
return inputArray;
}
If you can, parse the strings first:
enum Unit { cm, m, km }
record Measurment(int Length, Unit Unit)
{
public override string ToString() => $"{Length}{Enum.GetName(typeof(Unit), Unit)}";
public double NormalizedLength => Unit switch
{
Unit.cm => Length * 0.001,
Unit.m => Length * 1.0,
Unit.km => Length * 1000.0,
_ => throw new NotImplementedException()
};
public static Measurment Parse(string source)
{
var digits = source.TakeWhile(char.IsDigit).Count();
var length = int.Parse(source.AsSpan(0, digits));
// switches with source.AsSpan(digits) in preview
var measure = source[..digits] switch
{
"cm" => Unit.cm,
"m" => Unit.m,
"km" => Unit.km,
_ => throw new NotImplementedException(),
};
return new Measurment(length, measure);
}
}
.
var result = data.Select(Measurment.Parse).OrderBy(x => x.NormalizedLength).ToList();
This lets you sort your measurments by NormalizedLength and ToString gets back the original string. Should be very fast, simple to extend with new units and you can make it fault-tolerant if you turn Parse into the TryParse pattern.
There's a NuGet package to manage parsing and manipulating SI units called UnitsNet.
If you install that package (via Add | NuGet Package, search for and select UnitsNet and install it), then you can write the following code:
(You'll need to add using UnitsNet; at the top of the code file first)
This also works with nm etc.
List<string> data = new List<string> { "1m", "1cm", "4km", "2cm" };
var result = data.OrderBy(Length.Parse).ToList();
Console.WriteLine(string.Join(", ", result));
This will output "1cm, 2cm, 1m, 4km"
You need custom sort using IComparable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApplication49
{
class Program
{
static void Main(string[] args)
{
List<string> data = new List<string> { "1m", "1cm", "4km", "2cm" };
List<string> results = data.Select(x => new SortDistance(x)).OrderBy(x => x).Select(x => x.value).ToList();
}
}
public class SortDistance : IComparable<SortDistance>
{
const string pattern = #"(?'number'\d+)(?'multiplier'.*)";
List<string> distanceOrder = new List<string>() { "cm", "m", "km" };
public string value { get; set; }
public int distance { get; set; }
public string multiplier { get; set; }
public SortDistance(string value)
{
this.value = value;
Match match = Regex.Match(value, pattern);
this.distance = int.Parse(match.Groups["number"].Value);
this.multiplier = match.Groups["multiplier"].Value;
}
public int CompareTo(SortDistance other)
{
if (this.multiplier == other.multiplier)
return this.distance.CompareTo(other.distance);
else
return distanceOrder.IndexOf(this.multiplier).CompareTo(distanceOrder.IndexOf(other.multiplier));
}
}
}
you can not sort using OrderBy.
You have to define the conversion first from all units to the smallest unit. for example m to cm, km to cm.....
so 1m euqals to 100 cm
then you have to iterate through your list and check each item's unit, get its equivalent to the smallest unit.
Create another list.
you can implement insertion sort to sort the items and add keep on inserting the item based on the comparison.
I have a problem. I have a list with 4 coins and 4 values. Now the List is sorted by name like this:
1. BTC---Value
2. ETH---Value
3. LTC---Value
4. USDT---Value
But now I want to get a List with only 2 coins left:
The last coin needs to be USDT and the first Coin is the coin with the highest value. So for example if all the coins have value 3 and BTC has value 4, then I want a List like this:
1. BTC---Value
2. USDT---Value
How can I do that, because I know how to sort by value, but not with all my preferences....
Can someone help me?
DETAILS
Even if USDT has the highest value, I want that coin at the last place. If you add another coin, it needs to just look at the highest value again (except for USDT) and place that coin at the top with USDT on second place!
Updated code based on comment by DubDub.
var intermediate = list.OrderBy(x=> x.Name=="USDT").ThenByDescending(x=>x.Value);
var result = new []{intermediate.First(),intermediate.Last()};
Example,
Scenario 1 : When there are more than 2 items
var list = new List<Coin>
{
new Coin{Name="USDT", Value = 29},
new Coin{Name="ETH", Value = 13},
new Coin{Name="LTC", Value = 21},
new Coin{Name="BTC", Value = 3},
};
Output
Scenario 2 : When there are only two items
var list = new List<Coin>
{
new Coin{Name="USDT", Value = 29},
new Coin{Name="LTC", Value = 21},
};
You could do it with Linq. This wouldn't modify the list; it would create a new enumerable sorted by your criteria.
var sortedCoins = coins.OrderBy(c => c.Name == "USDT")
.ThenByDescending(c => c.Value);
Using the following stolen class from a previous answer that is now gone so I'm not sure who to give credit to, but you should be able to do the following.
Coin class
public class Coin
{
public string Name { get; set; }
public double Value { get; set; }
}
Actual code to handle list
List<Coin> list = new List<Coin>
{
new Coin{Name="USDT", Value = 29},
new Coin{Name="ETH", Value = 13},
new Coin{Name="LTC", Value = 21},
new Coin{Name="BTC", Value = 3},
};
// Take out USDT coin
Coin USDTcoin = list.Find(coin => coin.Name == "USDT");
list.RemoveAt(list.FindIndex(coin => coin.Name == "USDT"));
// Order list
list = list.OrderByDescending(coin => coin.Value).ToList();
// Make new list
list = new List<Coin>
{
list[0],
USDTcoin
};
This way:
class Program
{
static void Main(string[] args)
{
List<Coin> coins = new List<Coin>()
{
new Coin ("BTC", 1),
new Coin ("ETH", 2),
new Coin ("LTC", 3),
new Coin ("USDT", 4),
};
Coin usdt = coins.First(x => x.Name == "USDT");
coins.Remove(usdt);
coins = new List<Coin>() { coins.OrderByDescending(x => x.Value).First(), usdt };
}
}
public class Coin
{
public string Name { get; set; }
public double Value { get; set; }
public Coin(string name, double value)
{
Name = name;
Value = value;
}
}
I have some data like
ID Sequence customIndex
1 1 0
2 2 0
3 3 2
4 4 1
5 5 0
I need to use sequence in order by when customIndex is zero other wise use customIndex.
So result should be ID in order of 1,2,4,3,5.
I need LINQ implementation using Lambda. I tried some solution but could not implement.
Posting duplicate and deleting previous one, because of wrong formatting the meaning of question got changed and I received bunch of negative votes.
Added code at dotnet fiddle:
https://stable.dotnetfiddle.net/fChl40
The answer is based on assumption, that CustomIndex is greater or equals to zero:
var result =
data.OrderBy(x => x.CustomIndex==0 ? x.Sequence :
data.Where(y => y.CustomIndex==0 && y.Sequence < x.Sequence)
.Max(y => (int?)y.Sequence))
.ThenBy(x => x.CustomIndex);
This is working for provided test data:
l.OrderBy(a => a.customIndex != 0 ?
list.Where(b => b.Sequence < a.Sequence && b.customIndex == 0)
.OrderByDescending(c => c.Sequence)
.FirstOrDefault()
.Sequence : a.Sequence)
.ThenBy(c=>c.customIndex )
.ToList();
The idea is to order non zero values by first preceding zero valued rows, and then by non zero values itself.
This is something I wanted:
public static void Main()
{
List<Data> data = new List<Data>();
data.Add(new Data{ Id=1, Sequence=1, CustomIndex=0});
data.Add(new Data{ Id=5, Sequence=5, CustomIndex=0});
data.Add(new Data{ Id=6, Sequence=6, CustomIndex=2});
data.Add(new Data{ Id=2, Sequence=2, CustomIndex=0});
data.Add(new Data{ Id=3, Sequence=3, CustomIndex=2});
data.Add(new Data{ Id=4, Sequence=4, CustomIndex=1});
data.Add(new Data{ Id=7, Sequence=7, CustomIndex=1});
int o = 0;
var result = data
.OrderBy(x=>x.Sequence).ToList()
.OrderBy((x)=> myCustomSort(x, ref o) )
;
result.ToList().ForEach(x=> Console.WriteLine(x.Id));
}
public static float myCustomSort(Data x, ref int o){
if(x.CustomIndex==0){
o = x.Sequence;
return x.Sequence ;
}
else
return float.Parse(o + "."+ x.CustomIndex);
}
Sample code: https://stable.dotnetfiddle.net/fChl40
I will refine it further
Based on your question and reply to my comment, I understand you need to clusterize the items' collection, then consider Sequence and CustomIndex on all items of each cluster.
Once clustered (split into blocks depending on a specific criterion) you can merge them back into a unique collection, but while doing that you can manipulate each cluster independently the way you need.
public static class extCluster
{
public static IEnumerable<KeyValuePair<bool, T[]>> Clusterize<T>(this IEnumerable<T> self, Func<T, bool> clusterizer)
{
// Prepare temporary data
var bLastCluster = false;
var cluster = new List<T>();
// loop all items
foreach (var item in self)
{
// Compute cluster kind
var bItemCluster = clusterizer(item);
// If last cluster kind is different from current
if (bItemCluster != bLastCluster)
{
// If previous cluster was not empty, return its items
if (cluster.Count > 0)
yield return new KeyValuePair<bool, T[]>(bLastCluster, cluster.ToArray());
// Store new cluster kind and reset items
bLastCluster = bItemCluster;
cluster.Clear();
}
// Add current item to cluster
cluster.Add(item);
}
// If previous cluster was not empty, return its items
if (cluster.Count > 0)
yield return new KeyValuePair<bool, T[]>(bLastCluster, cluster.ToArray());
}
}
// sample
static class Program
{
public class Item
{
public Item(int id, int sequence, int _customIndex)
{
ID = id; Sequence = sequence; customIndex = _customIndex;
}
public int ID, Sequence, customIndex;
}
[STAThread]
static void Main(string[] args)
{
var aItems = new[]
{
new Item(1, 1, 0),
new Item(2, 2, 0),
new Item(3, 3, 2),
new Item(4, 4, 1),
new Item(5, 5, 0)
};
// Split items into clusters
var aClusters = aItems.Clusterize(item => item.customIndex != 0);
// Explode clusters and sort their items
var result = aClusters
.SelectMany(cluster => cluster.Key
? cluster.Value.OrderBy(item => item.customIndex)
: cluster.Value.OrderBy(item => item.Sequence));
}
}
It ain't pretty, but it exemplifies what you were asking for, I think:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<Data> data = new List<Data>();
data.Add(new Data { Id = 1, Sequence = 1, CustomIndex = 0 });
data.Add(new Data { Id = 2, Sequence = 2, CustomIndex = 0 });
data.Add(new Data { Id = 3, Sequence = 3, CustomIndex = 2 });
data.Add(new Data { Id = 4, Sequence = 4, CustomIndex = 1 });
data.Add(new Data { Id = 5, Sequence = 5, CustomIndex = 0 });
//List of items where the sequence is what counts
var itemsToPlaceBySequence = data.Where(x => x.CustomIndex == 0).OrderBy(x => x.Sequence).ToList();
//List of items where the custom index counts
var itemsToPlaceByCustomIndex = data.Where(x => x.CustomIndex != 0).OrderBy(x => x.CustomIndex).ToList();
//Array to hold items
var dataSlots = new Data[data.Count];
//Place items by sequence
foreach(var dataBySequence in itemsToPlaceBySequence) {
dataSlots[dataBySequence.Sequence - 1] = dataBySequence ;
}
//Find empty data slots and place remaining items in CustomIndex order
foreach (var dataByCustom in itemsToPlaceByCustomIndex) {
var index = dataSlots.ToList().IndexOf(null);
dataSlots[index] = dataByCustom ;
}
var result = dataSlots.ToList();
result.ForEach(x => Console.WriteLine(x.Id));
var discard = Console.ReadKey();
}
public class Data
{
public int Id { get; set; }
public int Sequence { get; set; }
public int CustomIndex { get; set; }
}
}
The ordering you want to do (order partly on CustomIndex and partly on Sequence) doesn't work like that. But this should be close to what you want. Order first by CustomIndex, and then by Sequence.
var result = data.OrderBy(x => x.CustomIndex).ThenBy(x => x.Sequence);
I have a list that contain instances of class A.
class A
{
public int Id;
public float Value;
}
List<A> Collection = new List<A>( ... );
I want order the list using
Collection.OrderBy(item => item.Value);
This should be working but for float numbers it messes up the ordering. It will produce
1.0, 1.5, 1.6, 10.5, 11.54, 3.4, 4, 6.6, 7
Where 10.5, 11.54 should be at bottom the list.
This approach works perfectly for if Value were int. Any clue?
Without creating a new list:
Collection.Sort((x,y) => x.Value.CompareTo(y.Value));
Try
List<A> Collection = new List<A>( ... );
List<A> lstOrderedA = Collection.OrderBy(item => item.Value).ToList();
Here lstOrderedA will have the ordered list you are looking for.
i use this code :
List<A> Collection = new List<A>()
{
new A(){Id=1,Value=1.0f},new A(){Id=1,Value=11.5f},new A(){Id=1,Value=1.6f},new A(){Id=1,Value=10.5f}
};
List<A> orderedList = Collection.OrderBy(i =>i.Value).ToList();
and it show
1.0,1.6,10.5,11.5.
This proves that it does work: https://dotnetfiddle.net/3ryECS
Linq however doesn't change the original list it returns a new sorted IEnumerable<A>
So after calling .OrderBy(a => a.Value) the value of collection stays the same.
As explained in another answer if you want to change the original list you should use Sort
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
class A
{
public int Id;
public float Value;
}
public static void Main()
{
var collection = new List<A>{
new A { Id = 1, Value = 1.0f },
new A { Id = 5, Value = 5.0f },
new A { Id = 6, Value = 6.0f },
new A { Id = 10, Value = 10.0f },
new A { Id = 11, Value = 11.0f },
new A { Id = -289, Value = -289.0f },
new A { Id = 123, Value = 123.0f },
new A { Id = 3, Value = 3.0f }
};
foreach (var a in collection.OrderBy(v => v.Value))
{
Console.WriteLine(a.Value);
}
}
}
which outputs:
-289
1
3
5
6
10
11
123
try this :
List<A> ResultList = Collection.OrderBy(item => item.Value).ToList();
U can use Linq query syntax too:
IEnumerable<A> c = from x in Collection
orderby x.Value
select x;
I have 3 sets in Linq, like this:
struct Index
{
string code;
int indexValue;
}
List<Index> reviews
List<Index> products
List<Index> pages
These lists have different code.
I want to merge these sets as following:
Take the first in reviews
Take the first in products
Take the first in pages
Take the second in reviews
-... and so on, note that these lists are not same-size.
How can I do this in Linq?
EDIT: Wait, is there a change to do this without .NET 4.0?
Thank you very much
You could use Zip to do your bidding.
var trios = reviews
.Zip(products, (r, p) => new { Review = r, Product = p })
.Zip(pages, (rp, p) => new { rp.Review, rp.Product, Page = p });
Edit:
For .NET 3.5, it's possible to implement Zip quite easily: but there are a few gotcha s. Jon Skeet has a great post series on how to implement LINQ to objects operators (for educational purposes), including this post, on Zip. The source code of the whole series, edulinq, can be found on Google Code.
The simple answer
To merge them into a common list without any common data, using the order they appear this, you can use the Zip method:
var rows = reviews
.Zip(products, (r, p) => new { Review = r, Product = p })
.Zip(pages, (rp, page) => new { rp.Review, rp.Product, Page = page });
The problem with this solution is that the lists must be identical length, or your result will be chopped to the shortest list of those original three.
Edit:
If you can't use .Net 4, check out Jon Skeet's blog posts on a clean-room implementation of Linq and His article on Zip in particular.
If you're using .Net 2, then try his library (possibly) or try LinqBridge
How to deal with different-lengthed lists
You can pre-pad the list to the desired length. I couldn't find an existing method to do this, so I'd use an extension method:
public static class EnumerableExtensions
{
public static IEnumerable<T> Pad<T>(this IEnumerable<T> source,
int desiredCount, T padWith = default(T))
{
// Note: Not using source.Count() to avoid double-enumeration
int counter = 0;
var enumerator = source.GetEnumerator();
while(counter < desiredCount)
{
yield return enumerator.MoveNext()
? enumerator.Current
: padWith;
++counter;
}
}
}
You can use it like this:
var paddedReviews = reviews.Pad(desiredLength);
var paddedProducts = products.Pad(desiredLength,
new Product { Value2 = DateTime.Now }
);
Full compiling sample and corresponding output
using System;
using System.Collections.Generic;
using System.Linq;
class Review
{
public string Value1;
}
class Product
{
public DateTime Value2;
}
class Page
{
public int Value3;
}
public static class EnumerableExtensions
{
public static IEnumerable<T> Pad<T>(this IEnumerable<T> source,
int desiredCount, T padWith = default(T))
{
int counter = 0;
var enumerator = source.GetEnumerator();
while(counter < desiredCount)
{
yield return enumerator.MoveNext()
? enumerator.Current
: padWith;
++counter;
}
}
}
class Program
{
static void Main(string[] args)
{
var reviews = new List<Review>
{
new Review { Value1 = "123" },
new Review { Value1 = "456" },
new Review { Value1 = "789" },
};
var products = new List<Product>()
{
new Product { Value2 = DateTime.Now },
new Product { Value2 = DateTime.Now.Subtract(TimeSpan.FromSeconds(5)) },
};
var pages = new List<Page>()
{
new Page { Value3 = 123 },
};
int maxCount = Math.Max(Math.Max(reviews.Count, products.Count), pages.Count);
var rows = reviews.Pad(maxCount)
.Zip(products.Pad(maxCount), (r, p) => new { Review = r, Product = p })
.Zip(pages.Pad(maxCount), (rp, page) => new { rp.Review, rp.Product, Page = page });
foreach (var row in rows)
{
Console.WriteLine("{0} - {1} - {2}"
, row.Review != null ? row.Review.Value1 : "(null)"
, row.Product != null ? row.Product.Value2.ToString() : "(null)"
, row.Page != null ? row.Page.Value3.ToString() : "(null)"
);
}
}
}
123 - 9/7/2011 10:02:22 PM - 123
456 - 9/7/2011 10:02:17 PM - (null)
789 - (null) - (null)
On use of the Join tag
This operation isn't a logical Join. This is because you're matching on index, not on any data out of each object. Each object would have to have other data in common (besides their position in the lists) to be joined in the sense of a Join that you would find in a relational database.