C#: Cast to Generic Interface with Base Type

Dynamically cross-join multiple different-size collections together in Linq (C#)

You could create an extension method like the following:

public static class EnumerableExtensions
{
public static IEnumerable<TValue []> Permutations<TKey, TValue>(this IEnumerable<TKey> keys, Func<TKey, IEnumerable<TValue>> selector)
{
var keyArray = keys.ToArray();
if (keyArray.Length < 1)
yield break;
TValue [] values = new TValue[keyArray.Length];
foreach (var array in Permutations(keyArray, 0, selector, values))
yield return array;
}

static IEnumerable<TValue []> Permutations<TKey, TValue>(TKey [] keys, int index, Func<TKey, IEnumerable<TValue>> selector, TValue [] values)
{
Debug.Assert(keys.Length == values.Length);

var key = keys[index];
foreach (var value in selector(key))
{
values[index] = value;
if (index < keys.Length - 1)
{
foreach (var array in Permutations(keys, index+1, selector, values))
yield return array;
}
else
{
yield return values.ToArray(); // Clone the array;
}
}
}
}

As an example, it could be used like:

    public static void TestPermutations()
{
int [][] seqence = new int [][]
{
new int [] {1, 2, 3},
new int [] {101},
new int [] {201},
new int [] {301, 302, 303},
};

foreach (var array in seqence.Permutations(a => a))
{
Debug.WriteLine(array.Aggregate(new StringBuilder(), (sb, i) => { if (sb.Length > 0) sb.Append(","); sb.Append(i); return sb; }));
}
}

and produce the following output:

1,101,201,301
1,101,201,302
1,101,201,303
2,101,201,301
2,101,201,302
2,101,201,303
3,101,201,301
3,101,201,302
3,101,201,303

Is that what you want?

Dynamically cross-join multiple different-size collections together in Linq (C#)

You could create an extension method like the following:

public static class EnumerableExtensions
{
public static IEnumerable<TValue []> Permutations<TKey, TValue>(this IEnumerable<TKey> keys, Func<TKey, IEnumerable<TValue>> selector)
{
var keyArray = keys.ToArray();
if (keyArray.Length < 1)
yield break;
TValue [] values = new TValue[keyArray.Length];
foreach (var array in Permutations(keyArray, 0, selector, values))
yield return array;
}

static IEnumerable<TValue []> Permutations<TKey, TValue>(TKey [] keys, int index, Func<TKey, IEnumerable<TValue>> selector, TValue [] values)
{
Debug.Assert(keys.Length == values.Length);

var key = keys[index];
foreach (var value in selector(key))
{
values[index] = value;
if (index < keys.Length - 1)
{
foreach (var array in Permutations(keys, index+1, selector, values))
yield return array;
}
else
{
yield return values.ToArray(); // Clone the array;
}
}
}
}

As an example, it could be used like:

    public static void TestPermutations()
{
int [][] seqence = new int [][]
{
new int [] {1, 2, 3},
new int [] {101},
new int [] {201},
new int [] {301, 302, 303},
};

foreach (var array in seqence.Permutations(a => a))
{
Debug.WriteLine(array.Aggregate(new StringBuilder(), (sb, i) => { if (sb.Length > 0) sb.Append(","); sb.Append(i); return sb; }));
}
}

and produce the following output:

1,101,201,301
1,101,201,302
1,101,201,303
2,101,201,301
2,101,201,302
2,101,201,303
3,101,201,301
3,101,201,302
3,101,201,303

Is that what you want?

How to join unknown number of lists in LINQ

UPDATE 5-15-17:

Just for the sake of recap what I am proposing is an example that we want to:

  1. Pass in a list of N number of Table objects.
  2. Pass in a list of N-1 join clauses of how to join them. EG: You have 2 tables you need a single join, 3 you need 2, and so on.
  3. We want to be to pass in a predicate to go up or down the chain to narrow scope.

What I would propose is to do all of this in SQL and pass into SQL an xml object that it can parse. However to keep it a little more simple to not deal with XML serialization too, let's stick with strings that are essentially one or many values to pass in. Say we have a structure going off of above like this:

/*
CREATE TABLE Customer ( Id INT IDENTITY, CustomerName VARCHAR(64), ProductId INT)
INSERT INTO Customer VALUES ('Acme', 1),('Widgets', 2)
CREATE TABLE Product (Id INT IDENTITY, ProductName VARCHAR(64), VehicleId INT)
Insert Into Product Values ('Shirt', 1),('Pants', 2)
CREATE TABLE VEHICLE (Id INT IDENTITY, VehicleName VARCHAR(64))
INSERT INTO dbo.VEHICLE VALUES ('Car'),('Truck')

CREATE TABLE Joins (Id INT IDENTITY, OriginTable VARCHAR(32), DestinationTable VARCHAR(32), JoinClause VARCHAR(32))
INSERT INTO Joins VALUES ('Customer', 'Product', 'ProductId = Id'),('Product', 'Vehicle', 'VehicleId = Id')

--Data as is if I joined all three tables
CustomerId CustomerName ProductId ProductName VehicleId VehicleName
1 Acme 1 Shirt 1 Car
2 Widgets 2 Pants 2 Truck
*/

This structure is pretty simplistic and everything is one to one key relationships versus it could have some other identifiers. The key to making things work is to maintain a table that describes HOW these tables relate. I called this table joins. Now I can create a dynamic proc like so:

CREATE PROC pDynamicFind
(
@Tables varchar(256)
, @Joins VARCHAR(256)
, @Predicate VARCHAR(256)
)
AS
BEGIN
SET NOCOUNT ON;

DECLARE @SQL NVARCHAR(MAX) =
'With x as
(
SELECT
a.Id
, {nameColumns}
From {joins}
Where {predicate}
)
SELECT *
From x
UNPIVOT (Value FOR TableName In ({nameColumns})) AS unpt
'
DECLARE @Tbls TABLE (id INT IDENTITY, tableName VARCHAR(256), joinType VARCHAR(16))
DECLARE @Start INT = 2
DECLARE @alphas VARCHAR(26) = 'abcdefghijklmnopqrstuvwxyz'

--Comma seperated into temp table (realistically most people create a function to do this so you don't have to do it over and over again)
WHILE LEN(@Tables) > 0
BEGIN
IF PATINDEX('%,%', @Tables) > 0
BEGIN
INSERT INTO @Tbls (tableName) VALUES (RTRIM(LTRIM(SUBSTRING(@Tables, 0, PATINDEX('%,%', @Tables)))))
SET @Tables = SUBSTRING(@Tables, LEN(SUBSTRING(@Tables, 0, PATINDEX('%,%', @Tables)) + ',') + 1, LEN(@Tables))
END
ELSE
BEGIN
INSERT INTO @Tbls (tableName) VALUES (RTRIM(LTRIM(@Tables)))
SET @Tables = NULL
END
END

--Have to iterate over this one seperately
WHILE LEN(@Joins) > 0
BEGIN
IF PATINDEX('%,%', @Joins) > 0
BEGIN
Update @Tbls SET joinType = (RTRIM(LTRIM(SUBSTRING(@Joins, 0, PATINDEX('%,%', @Joins))))) WHERE id = @Start
SET @Joins = SUBSTRING(@Joins, LEN(SUBSTRING(@Joins, 0, PATINDEX('%,%', @Joins)) + ',') + 1, LEN(@Joins))
SET @Start = @Start + 1
END
ELSE
BEGIN
Update @Tbls SET joinType = (RTRIM(LTRIM(@Joins))) WHERE id = @Start
SET @Joins = NULL
SET @Start = @Start + 1
END
END

DECLARE @Join VARCHAR(256) = ''
DECLARE @Cols VARCHAR(256) = ''

--Determine dynamic columns and joins
Select
@Join += CASE WHEN joinType IS NULL THEN t.tableName + ' ' + SUBSTRING(@alphas, t.id, 1)
ELSE ' ' + joinType + ' JOIN ' + t.tableName + ' ' + SUBSTRING(@alphas, t.id, 1) + ' ON ' + SUBSTRING(@alphas, t.id-1, 1) + '.' + REPLACE(j.JoinClause, '= ', '= ' + SUBSTRING(@alphas, t.id, 1) + '.' )
END
, @Cols += CASE WHEN joinType IS NULL THEN t.tableName + 'Name' ELSE ' , ' + t.tableName + 'Name' END
From @Tbls t
LEFT JOIN Joins j ON t.tableName = j.DestinationTable

SET @SQL = REPLACE(@SQL, '{joins}', @Join)
SET @SQL = REPLACE(@SQL, '{nameColumns}', @Cols)
SET @SQL = REPLACE(@SQL, '{predicate}', @Predicate)

--PRINT @SQL
EXEC sp_executesql @SQL
END
GO

I now have a medium for finding things that makes it stubbed query so to speak that I can replace the source of the from statement, what I query on, what value I use to query on. I would get results from it like this:

EXEC pDynamicFind 'Customer, Product', 'Inner', 'CustomerName = ''Acme'''
EXEC pDynamicFind 'Customer, Product, Vehicle', 'Inner, Inner', 'VehicleName = ''Car'''

Now what about setting that up in EF and using it in code? Well you can add procs to EF and get data from this as context. The answer that this addresses is that I am essentially giving back a fixed object now despite however many columns I may add. If my pattern is always going to be '(table)name' to N numbers of tables I can normalize my result by unpivoting and then just getting N number of rows for however many tables I have. Thus performance may be worse as you get larger result sets but the potential to make however many joins you want as long as similar structure is used is possible.

The point I am making though is that SQL is ultimately getting your data and doing crazy joins that result from Linq is at times more work than it's worth. But if you do have a small result set and a small db, you are probably fine. This is just an example of how you would get completely different objects in SQL using dynamic sql and how fast it can do something once the code for the proc is written. This is just one way to skin a cat of which I am sure there are many. The problem is whatever road you go down with dynamic joins or a method of getting things out is going to require some type of normalization standard, factory pattern or something where it says I can have N inputs that always yield the same X object no matter what. I do this through a vertical result set, but if you want a different column than say 'name' you are going to have to code more for that as well. However the way I built this if you want the description but say wanted to do a predicate for a date field, this would be fine with that.

Merge data into single Dictionary after cross join of two list in c#

Perhaps you mean you want your output "row" to be a single dictionary containing all of x and all of y. It could be like:

var result= list1.SelectMany(
x=> list2,
(x,y) => new Dictionary<WhateverType,YouHave>(x.Concat(Y))
);

It takes all of the KeyValuePair<WhateverTypr,YouHave> in x and y and makes an IEnumerable<KeyValuePair<WhateverType,YouHave>> from them, then makes a new Dictionary that contains all those keyvaluepairs

The types of the dictionary will need to be consistent; you can't have x be a Dictionary<string,int> and y be a Dictionary<int,string> for example

Distribute quantities into buckets - Not evenly

Sure, there is a solution. You could use Dirichlet Distribution for such task. Property of the distribution is that

Sumi xi = 1

So solution would be to sample 200 (equal the number of buckets) random values from Dirichlet, and then multiply each value by 950,000 (or whatever total inventory is) and that would give you number of items per bucket. If you want non-uniform sampling, you could tweak alpha in the Dirichlet sampling.

Items per bucket shall be rounded up/down, of course, but that is pretty trivial

I have Dirichlet sampling in C# somewhere, if you struggle to implement it - tell me and I would dig it out

UPDATE

I found some code, .NET Core 2, below is the excerpt. I used to sample Dirichlet RNs with the sample alphas, making all of them different is trivial.

//
// Dirichlet sampling, using Gamma sampling from Math .NET
//

using MathNet.Numerics.Distributions;
using MathNet.Numerics.Random;

static void SampleDirichlet(double alpha, double[] rn)
{
if (rn == null)
throw new ArgumentException("SampleDirichlet:: Results placeholder is null");

if (alpha <= 0.0)
throw new ArgumentException($"SampleDirichlet:: alpha {alpha} is non-positive");

int n = rn.Length;
if (n == 0)
throw new ArgumentException("SampleDirichlet:: Results placeholder is of zero size");

var gamma = new Gamma(alpha, 1.0);

double sum = 0.0;
for(int k = 0; k != n; ++k) {
double v = gamma.Sample();
sum += v;
rn[k] = v;
}

if (sum <= 0.0)
throw new ApplicationException($"SampleDirichlet:: sum {sum} is non-positive");

// normalize
sum = 1.0 / sum;
for(int k = 0; k != n; ++k) {
rn[k] *= sum;
}
}


Related Topics



Leave a reply



Submit