I have a list as below
PillarId Quarter Feature
1 Q12106 France
1 Q12016 Germany
1 Q22016 Italy
1 Q32016 Russia
2 Q22016 India
2 Q32016 USA
3 Q22016 China
3 Q32016 Australia
3 Q32016 New Zeland
3 Q42016 Japan
I want convert this into a list which looks like this
pillarId Q12016 Q22016 Q32016 Q42016
1 France Italy Russia
1 Germany
2 India USA
3 China Australia Japan
3 New Zeland
Can anybody suggest some sample code
Thanks
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication16
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("PillarId", typeof(int));
dt.Columns.Add("Quarter", typeof(string));
dt.Columns.Add("Feature", typeof(string));
dt.Rows.Add(new object[] {1, "Q12116", "France"});
dt.Rows.Add(new object[] {1, "Q12116", "Germany"});
dt.Rows.Add(new object[] {1, "Q22116", "Italy"});
dt.Rows.Add(new object[] {1, "Q32116", "Russia"});
dt.Rows.Add(new object[] {2, "Q22116", "India"});
dt.Rows.Add(new object[] {2, "Q32116", "USA"});
dt.Rows.Add(new object[] {3, "Q22116", "China"});
dt.Rows.Add(new object[] {3, "Q32116", "Austrailia"});
dt.Rows.Add(new object[] {3, "Q32116", "New Zeland"});
dt.Rows.Add(new object[] {3, "Q42116", "Japan"});
string[] uniqueQuarters = dt.AsEnumerable().Select(x => x.Field<string>("Quarter")).Distinct().ToArray();
var groups = dt.AsEnumerable()
.GroupBy(x => x.Field<int>("PillarId")).Select(x => x.GroupBy(y => y.Field<string>("Quarter")).Select(z => new { id = x.Key, quarter = z.Key, feature = z.Select((a,i) => new { feature = a.Field<string>("Feature"), index = i}).ToList()}).ToList()).ToList();
DataTable pivot = new DataTable();
pivot.Columns.Add("PillarId", typeof(int));
foreach (string quarter in uniqueQuarters)
{
pivot.Columns.Add(quarter, typeof(string));
}
foreach (var group in groups)
{
int maxNewRows = group.Select(x => x.feature.Count()).Max();
for (int index = 0; index < maxNewRows; index++)
{
DataRow newRow = pivot.Rows.Add();
foreach (var row in group)
{
newRow["PillarId"] = row.id;
newRow[row.quarter] = row.feature.Skip(index) == null || !row.feature.Skip(index).Any() ? "" : row.feature.Skip(index).First().feature;
}
}
}
}
}
}
This should work.
public struct temp
{
public int PillarID;
public string Quarter;
public string Feature;
}
static void Main(string[] args)
{
List<temp> list = new List<temp>
{
new temp {PillarID = 1, Quarter= "Q12106", Feature = "France"},
new temp {PillarID = 1, Quarter= "Q12106", Feature = "Germany"},
new temp {PillarID = 1, Quarter= "Q22016", Feature = "Italy"},
new temp {PillarID = 1, Quarter= "Q32016", Feature = "Russia"},
new temp {PillarID = 2, Quarter= "Q22016", Feature = "India"},
new temp {PillarID = 2, Quarter= "Q32016", Feature = "USA"},
new temp {PillarID = 3, Quarter= "Q22016", Feature = "China"},
new temp {PillarID = 3, Quarter= "Q32016", Feature = "Australia"},
new temp {PillarID = 3, Quarter= "Q32016", Feature = "New Zeland"},
new temp {PillarID = 3, Quarter= "Q42016", Feature = "Japan"}
};
IEnumerable<IGrouping<string, temp>> byQuarter = list.GroupBy(x => x.Quarter);
Console.WriteLine(byQuarter.ToString());
}
It does order them in the wanted way, but displaying it isn't so simple. I'm currently trying to make it display nicely
You can use dynamic keyword to create object as per your requirement at run time. I am not inserting multiple entries for a pillerId. In my case i am adding multiple features in quarter field for a given pillerId. Please look into below code for the same:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Dynamic;
namespace Test
{
public class Piller
{
public int PillarId;
public string Quarter;
public string Feature;
public static List<dynamic> Generate()
{
List<Piller> pillers = new List<Piller>();
pillers.Add(new Piller { PillarId = 1, Quarter = "Q12106", Feature = "France" });
pillers.Add(new Piller { PillarId = 1, Quarter = "Q12106", Feature = "Germany" });
pillers.Add(new Piller { PillarId = 1, Quarter = "Q22016", Feature = "Italy" });
pillers.Add(new Piller { PillarId = 1, Quarter = "Q32016", Feature = "Russia" });
pillers.Add(new Piller { PillarId = 2, Quarter = "Q22016", Feature = "Italy" });
pillers.Add(new Piller { PillarId = 2, Quarter = "Q32016", Feature = "USA" });
pillers.Add(new Piller { PillarId = 3, Quarter = "Q22016", Feature = "China" });
pillers.Add(new Piller { PillarId = 3, Quarter = "Q32016", Feature = "Australia" });
pillers.Add(new Piller { PillarId = 3, Quarter = "Q32016", Feature = "New Zeland" });
pillers.Add(new Piller { PillarId = 3, Quarter = "Q42016", Feature = "Japan" });
var pillerIds = (from p in pillers
select p.PillarId).Distinct();
var quarters = (from p in pillers
select p.Quarter).Distinct();
List<dynamic> transformedData = new List<dynamic>();
foreach (var pillerId in pillerIds)
{
var data = new ExpandoObject() as IDictionary<string, Object>;
data.Add("pillerId",pillerId);
foreach (var quarter in quarters)
{
var features = (from p in pillers
where p.PillarId == pillerId && p.Quarter == quarter
select p.Feature);
data.Add(quarter,features);
}
transformedData.Add(data);
}
return transformedData;
}
public static void Print(List<dynamic> data)
{
var dic = data[0] as IDictionary<string, Object>;
foreach (var field in dic.Keys)
{
Console.Write(field+" ");
}
Console.WriteLine();
foreach (dynamic record in data)
{
dic = record as IDictionary<string, Object>;
foreach (var field in dic.Keys)
{
if (field == "pillerId")
Console.Write(dic[field] + " ");
else
{
var value = dic[field];
if (value == null)
{
Console.Write(" ");
}
else
{
StringBuilder sb = new StringBuilder();
foreach (var item in (value as IEnumerable<string>))
{
if (sb.Length == 0)
sb.Append(item);
else
sb.Append(","+item);
}
Console.Write(sb.ToString());
}
}
Console.Write(" ");
}
Console.WriteLine();
}
}
}
}
Related
Here's a list, think of it as rows and columns where rows are going down and columns are side ways. the column count will always be the same for all rows.
var dataValues = new List<List<string>>()
{
//row 1
new List<string>(){"A","12","X","P8" },
//row 2
new List<string>(){"B","13","Y","P7" },
//row 3
new List<string>(){"C","12","Y","P6" },
//row 4
new List<string>(){"A","14","X","P5" },
//....
new List<string>(){"D","15","Z","P4" },
new List<string>(){"A","13","X","P3" },
new List<string>(){"B","14","Y","P2" },
new List<string>(){"C","13","Z","P1" },
};
The user providers a list of indexes to group by.
var userParam= new List<int>() { 0, 2 };
my question is how do i dynamically group dataValues by the userParam where user param is n amount of index. In the example above it will gorup by the first column and the 3rd. However the index can change and the amount of indexes can change aswell
example
var userParam2 = new List<int>() { 0, 2};
var userParam3 = new List<int>() { 0};
var userParam4 = new List<int>() { 0,1,2};
i know how to group by when i know how many indexes there will be (the the case below it's 2 index parameters), however when it's dynamic (x amount) then i do not know how to do this
var result = dataValues.GroupBy(e => new { G1 = e[userParam2 [0]], G2 = e[userParam2 [1]] });
You could use a Custom Comparer to achieve this :
1 - Declaration of GroupByComparer that inherit from IEqualityComparer :
public class GroupByComparer : IEqualityComparer<List<string>>
{
private static List<int> _intList;
public GroupByComparer(List<int> intList)
{
_intList = intList;
}
public bool Equals(List<string> x, List<string> y)
{
foreach (int item in _intList)
{
if (x[item] != y[item])
return false;
}
return true;
}
public int GetHashCode(List<string> obj)
{
int hashCode = 0;
foreach (int item in _intList)
{
hashCode ^= obj[item].GetHashCode() + item;
}
return hashCode;
}
}
2 - Call group by with EqualityComparer like :
var userParam = new List<int>() { 0, 2 };
var result = dataValues.GroupBy(e => e, new GroupByComparer(userParam));
I hope you find this helpful.
I believe i have something but this looks slow please let me know if there is anyway better of doing this.
var userParams = new List<int>() { 0, 2 };
var dataValues = new List<List<string>>()
{
new List<string>(){"A","12","X","P8" },
new List<string>(){"B","13","Y","P7" },
new List<string>(){"C","12","Y","P6" },
new List<string>(){"A","14","X","P5" },
new List<string>(){"D","15","Z","P4" },
new List<string>(){"A","13","X","P3" },
new List<string>(){"B","14","Y","P2" },
new List<string>(){"C","13","Z","P1" },
};
var result = new List<(List<string> Key, List<List<string>> Values)>();
result.Add((new List<string>(), dataValues));
for (int index = 0; index < userParams.Count; index++)
{
var currentResult = new List<(List<string> Key, List<List<string>> Values)>();
foreach (var item in result)
{
foreach (var newGroup in item.Values.GroupBy(e => e[userParams[index]]))
{
var newKey = item.Key.ToList();
newKey.Add(newGroup.Key);
currentResult.Add((newKey, newGroup.ToList()));
}
}
result = currentResult;
}
foreach(var res in result)
{
Console.WriteLine($"Key: {string.Join(#"\", res.Key)}, Values: {string.Join(" | ", res.Values.Select(e=> string.Join(",",e)))}");
}
final result
Key: A\X, Values: A,12,X,P8 | A,14,X,P5 | A,13,X,P3
Key: B\Y, Values: B,13,Y,P7 | B,14,Y,P2
Key: C\Y, Values: C,12,Y,P6
Key: C\Z, Values: C,13,Z,P1
Key: D\Z, Values: D,15,Z,P4
This question already has answers here:
Is there a good LINQ way to do a cartesian product?
(3 answers)
Closed 4 years ago.
I'm looking to get the Cartesian Product of an arbitrary number of objects in c#. My situation is slightly unusual - my inputs are not lists of base types, but objects which have a property that's a list of base types.
My input and output objects are as follows:
public class Input
{
public string Label;
public List<int> Ids;
}
public class Result
{
public string Label;
public int Id;
}
Some sample input data:
var inputs = new List<Input>
{
new Input { Label = "List1", Ids = new List<int>{ 1, 2 } },
new Input { Label = "List2", Ids = new List<int>{ 2, 3 } },
new Input { Label = "List3", Ids = new List<int>{ 4 } }
};
And my expected output object:
var expectedResult = new List<List<Result>>
{
new List<Result>
{
new Result{Label = "List1", Id = 1},
new Result{Label = "List2", Id = 2},
new Result{Label = "List3", Id = 4}
},
new List<Result>
{
new Result{Label = "List1", Id = 1},
new Result{Label = "List2", Id = 3},
new Result{Label = "List3", Id = 4}
},
new List<Result>
{
new Result{Label = "List1", Id = 2},
new Result{Label = "List2", Id = 2},
new Result{Label = "List3", Id = 4}
},
new List<Result>
{
new Result{Label = "List1", Id = 2},
new Result{Label = "List2", Id = 3},
new Result{Label = "List3", Id = 4}
}
};
If I knew the number of items in 'inputs' in advance I could do this:
var knownInputResult =
from id1 in inputs[0].Ids
from id2 in inputs[1].Ids
from id3 in inputs[2].Ids
select
new List<Result>
{
new Result { Id = id1, Label = inputs[0].Label },
new Result { Id = id2, Label = inputs[1].Label },
new Result { Id = id3, Label = inputs[2].Label },
};
I'm struggling to adapt this to an arbitrary number of inputs - is there a possible way to do this?
I consider this duplicate of question linked in comments, but since it was reopened and you struggle to adapt that question to your case, here is how.
First grab function by Eric Lippert from duplicate question as is (how it works is explained there):
public static class Extensions {
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] { item })
);
}
}
Then flatten your input. Basically just attach corresponding label to each id:
var flatten = inputs.Select(c => c.Ids.Select(r => new Result {Label = c.Label, Id = r}));
Then run cartesian product and done:
// your expected result
var result = flatten.CartesianProduct().Select(r => r.ToList()).ToList();
I'm not proud of the amount of time I spent messing with this, but it works.
It's basically black magic, and I would replace it the first chance you get.
public static List<List<Result>> Permutate(IEnumerable<Input> inputs)
{
List<List<Result>> results = new List<List<Result>>();
var size = inputs.Select(inp => factorial_WhileLoop(inp.Ids.Count)).Aggregate((item, carry) => item + carry) - 1;
for (int i = 0; i < size; i++) results.Add(new List<Result>());
foreach (var input in inputs)
{
for (int j = 0; j < input.Ids.Count; j++)
{
for (int i = 0; i < (size / input.Ids.Count); i++)
{
var x = new Result() { Label = input.Label, Id = input.Ids[j] };
results[(input.Ids.Count * i) + j].Add(x);
}
}
}
return results;
}
public static int factorial_WhileLoop(int number)
{
var result = 1;
while (number != 1)
{
result = result * number;
number = number - 1;
}
return result;
}
Seems like this is the kind of thing that would have already been answered but I'm unable to find it.
I have following table:
Id Parent Text
-----------------------
1 NULL A
2 1 B
3 2 C
4 3 D
5 NULL E
6 5 F
7 6 G
Now I want to have the result like this: (List<string>)
A
A > B
A > B > C
A > B > C > D
E
E > F
E > F > G
But the problem is that, this project that I am working on it, uses database first, I mean there's no navigation property, Parent is type of string not IEnumerable<T>.
What I have done so far:
var list = new List<string>();
string e2 = string.Empty;
foreach (var item in query)
{
string e1 = string.Empty;
if (item.Parent == null)
{
list.Add(p.Text);
e2 = item.Text;
}
else
{
foreach (var subItem in query.Where(t => t.Id == p.Parent))
{
if (subItem.Id != 1)
{
e1 = e2 + " > " + subItem.Text;
}
else
{
e1 = subItem.Text;
}
}
list.Add(e1 + " > " + p.Text);
}
}
Use recursive algorithm
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static List<string> list = new List<string>();
static DataTable dt = new DataTable();
static void Main(string[] args)
{
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("Parent", typeof(int));
dt.Columns["Parent"].AllowDBNull = true;
dt.Columns.Add("Text", typeof(string));
dt.Rows.Add(new object[] {1, null, "A"});
dt.Rows.Add(new object[] {2, 1, "B"});
dt.Rows.Add(new object[] {3, 2, "C"});
dt.Rows.Add(new object[] {4, 3, "D"});
dt.Rows.Add(new object[] {5, null, "E"});
dt.Rows.Add(new object[] {6, 5, "F"});
dt.Rows.Add(new object[] {7, 6, "G"});
GetRecursiveChildren(null, new List<string>());
foreach (string row in list)
{
Console.WriteLine(row);
}
Console.ReadLine();
}
static void GetRecursiveChildren(int? parent, List<string> parents)
{
foreach (DataRow row in dt.AsEnumerable().Where(x => x.Field<int?>("Parent") == parent))
{
string text = row.Field<string>("Text");
List<string> newParents = new List<string>();
newParents.AddRange(parents);
newParents.Add(text);
list.Add(string.Join(" > ",newParents));
int child = row.Field<int>("Id");
GetRecursiveChildren(child, newParents);
}
}
}
}
This works for me:
var source = new []
{
new { Id = 1, Parent = (int?)null, Text = "A" },
new { Id = 2, Parent = (int?)1, Text = "B" },
new { Id = 3, Parent = (int?)2, Text = "C" },
new { Id = 4, Parent = (int?)3, Text = "D" },
new { Id = 5, Parent = (int?)null, Text = "E" },
new { Id = 6, Parent = (int?)5, Text = "F" },
new { Id = 7, Parent = (int?)6, Text = "G" }
};
var lookup = source.ToLookup(x => x.Parent);
Func<int?, IEnumerable<string>> recurse = null;
recurse = p =>
lookup[p].SelectMany(x => new [] { x.Text }
.Concat(recurse(x.Id).Select(y => $"{x.Text} > {y}")));
foreach (string x in recurse(null))
{
Console.WriteLine(x);
}
I get:
A
A > B
A > B > C
A > B > C > D
E
E > F
E > F > G
I have the following code that I am using to put data into a relational table using Entity Framework:
public IList<Objective> createObjectives()
{
var objectiveNames = new[]
{
"Objective 1",
"Objective 2",
"Objective 3",
"Objective 4",
"Objective 5",
"Objective 6",
"Objective 7",
"Objective 8"
};
var objectives = objectiveNames.Select(o => new Objective
{
ObjectiveSeq = ??,
Name = o,
Description = o + " Description",
ModifiedDate = DateTime.Now
}
);
return objectives.ToList();
}
I have a new field in my table name ObjectiveSeq. How can I modify my LINQ to insert a sequential number in that field starting from 1.
var objectives = objectiveNames.Select((o, index) => new Objective
{
ObjectiveSeq = index,
Name = o,
Description = o + " Description",
ModifiedDate = DateTime.Now
}
);
There's an overload to the Select function. You can find it here.
Enumerable.Select<TSource, TResult> Method (IEnumerable<TSource>, Func<TSource, Int32, TResult>)
Take a look at the sample shown in the page.
string[] fruits = { "apple", "banana", "mango", "orange", "passionfruit", "grape" };
var query = fruits.Select((fruit, index) => new { index, str = fruit });
foreach (var obj in query)
{
Console.WriteLine("{0}", obj);
}
You can see that we are using (fruit, index). You are selecting both the element and an index.
The output will be
{ index = 0, str = apple }
{ index = 1, str = banana }
{ index = 2, str = mango }
{ index = 3, str = orange }
{ index = 4, str = passionfruit }
{ index = 5, str = grape }
Given this example data (in .NET classes where Po, Sku, Qty are properties):
PO, Sku, Qty
1,ABC,1
1,DEF,2
1,GHI,1
1,QWE,1
1,ASD,1
1,ZXC,5
1,ERT,1
2,QWE,1
2,ASD,11
2,ZXC,1
3,ERT,1
3,DFG,1
3,DFH,1
3,CVB,4
3,VBN,1
3,NMY,1
I need to transform it into a fixed column format, with a max of 5 SKUs per line (repeating the PO if needed for > 5):
PO, SkuA, QtyA, SkuB, QtyB, SkuC, QtyC, SkuD, QtyD, SkuE, QtyE
1, ABC, 1, DEF, 2, GHI, 1, QWE, 1, ASD, 1
1, ZXC, 5, ERT, 1, , , , , ,
2, QWE, 1, ASD, 11, ZXC, 1, , , ,
3, ERT, 1, DFG, 1, DFH, 1, CVB, 4, VBN, 1
3, NMY, 1, , , , , , , ,
Output can be CSV (which is what I'm outputting), or .NET classes - no matter there. Is there a simple way to do this in Linq by grouping by PO, then by counts of 5?
EDIT: I have no control of over the destination format. And for anyone interested, it's VendorNet and VendorBridge that require this nonsense.
Firstly, here's the query that will generate the correct hierarchy of objects. I'm using anonymous types but it's easy enough to change it to use your own proper classes.
var query = yourData
.GroupBy
(
x => x.PO
)
.SelectMany
(
x => x.Select
(
(y, i) => new { y.PO, y.Sku, y.Qty, Key = i / 5 }
)
)
.GroupBy
(
x => new { x.PO, x.Key }
);
Using LINQ to create the CSV from the query results is bit of a hack, but it gets the job done. (The "benefit" of using LINQ is that you could chain the original query and the CSV generation into a single, massive statement, should you wish.)
IEnumerable<string> csvLines = query
.Select
(
x => x.Aggregate
(
new { Count = 0, SB = new StringBuilder() },
(a, y) => new
{
Count = a.Count + 1,
SB = ((a.SB.Length == 0) ? a.SB.Append(y.PO) : a.SB)
.Append(", ").Append(y.Sku).Append(", ").Append(y.Qty)
},
a => a.SB.ToString() + string.Join(", , ", new string[6 - a.Count])
)
);
string csv = string.Join(Environment.NewLine, csvLines.ToArray());
In my opinion, creating the CSV without using LINQ makes the code much more readable:
StringBuilder sb = new StringBuilder();
foreach (var group in query)
{
int count = 0;
foreach (var item in group)
{
if (count++ == 0)
{
sb.Append(item.PO);
}
sb.Append(", ").Append(item.Sku).Append(", ").Append(item.Qty);
}
while (count++ < 5)
{
sb.Append(", , ");
}
sb.Append(Environment.NewLine);
}
string csv = sb.ToString();
Here you go. I didn't format the output the way you wanted. But this should give you an idea of how to pivot rows. Hope this helps :-)
public class MyClass
{
public int PO { get; set; }
public String SKU { get; set; }
public int Qty { get; set; }
public static IEnumerable<MyClass> GetList()
{
return new List<MyClass>()
{
new MyClass {PO = 1, SKU = "ABC", Qty = 1},
new MyClass {PO = 1, SKU = "DEF", Qty = 2},
new MyClass {PO = 1, SKU = "GHI", Qty = 1},
new MyClass {PO = 1, SKU = "QWE", Qty = 1},
new MyClass {PO = 1, SKU = "ASD", Qty = 1},
new MyClass {PO = 1, SKU = "ZXC", Qty = 5},
new MyClass {PO = 1, SKU = "ERT", Qty = 1},
new MyClass {PO = 2, SKU = "QWE", Qty = 1},
new MyClass {PO = 2, SKU = "ASD", Qty = 1},
new MyClass {PO = 2, SKU = "ZXC", Qty = 5},
};
}
}
EDIT: I've fixed the query based on Luke's comment
var lQuery =
MyClass.GetList()
.GroupBy(pArg => pArg.PO)
.Select(pArg => new
{
Test = pArg.Select((pArg1, pId) =>
new {ID = (pId / 5),
pArg1.PO, pArg1.SKU, pArg1.Qty})
.GroupBy(pArg1 => pArg1.ID)
.Select(pArg1 =>
pArg1.Aggregate(pArg.Key.ToString(),
(pSeed, pCur) =>
pSeed + pCur.SKU + ","))
});