Using multiple channels, what am I doing wrong? - c#

I want to create an array of Tasks, called listTask, with each element of listTask is a Task of type A, Task of type A is created by the function Task.WhenAll. then I do await Task.WhenAll(listTask) But the program does not perform the work in the listTask array. I set debug and those tasks were completed, I don't understand why
namespace Ding.LearningNewThings
{
public class MultiChannelTask
{
public static async Task RunMiltipleChannel()
{
ConcurrentDictionary<int, Channel<Position>> _dataChannels = new ConcurrentDictionary<int, Channel<Position>>();
var listPlace = Place.InitData();
var numberOfPlace = listPlace.Count();
for (int i = 0; i < listPlace.Count(); i++)
{
_dataChannels.TryAdd(i, Channel.CreateUnbounded<Position>());
}
Task[] listStationTask = new Task[numberOfPlace];
for (var j = 0; j < numberOfPlace; j++)
{
var listTask = new Task[2];
var placeOuter = listPlace[j];
listTask[0] = Task.Run(async () =>
{
int IndexOfPlace = j;
var place = new Place()
{
ID = placeOuter.ID,
Name = placeOuter.Name
};
Channel<Position> dataChannel;
var r = new Random();
if (_dataChannels.TryGetValue(IndexOfPlace, out dataChannel))
{
var position = new Position()
{
PlaceID = place.ID,
PlaceName = place.Name,
ID = r.Next(1, 100)
};
await dataChannel.Writer.WriteAsync(position);
Console.WriteLine($"Push postion ID {position.ID}, Place ID {position.PlaceID}");
}
});
listTask[1] = Task.Run(async () =>
{
var IndexOfPlace = j;
Channel<Position> dataChannel;
var r = new Random();
if (_dataChannels.TryGetValue(IndexOfPlace, out dataChannel)) {
var position = await dataChannel.Reader.ReadAsync();
Console.WriteLine($"Get postion ID {position.ID}, Place ID {position.PlaceID}");
}
});
listStationTask[j] = Task.WhenAll(listTask);
}
await Task.WhenAll(listStationTask);
}
}
public class Place
{
public int ID { get; set; }
public string Name { get; set; }
public static List<Place> InitData()
{
var listData = new List<Place>();
for (int i = 0; i < 10; i++)
{
var data = new Place()
{
ID = i,
Name = $"Postion{i}",
};
listData.Add(data);
}
return listData;
}
}
public class Position
{
public int ID { get; set; }
public int PlaceID { get; set; }
public string PlaceName { get; set; }
public string Name { get; set; }
public static List<Position> InitData()
{
var listData = new List<Position>();
for (int i = 0; i < 10; i++)
{
var data = new Position()
{
ID = i,
Name = $"Postion{i}"
};
listData.Add(data);
}
return listData;
}
}
}
I seem the task have had done ahead of intended. Sometimes it works, but I don't know why the job always completes without running in the list task code.

Related

Creating MongoDB map-reduce from aggregation pipeline in .net

I need help converting an aggregation pipeline to map-reduce. I know that map-reduce is deprecated, however, I need it. I am using mongoDB version 4.4 and MongoDB.Driver version 2.11.0, so it still should be available. I got the following aggregation function
PipelineDefinition<Airplane, BsonDocument> pipeline = new BsonDocument[]
{
new BsonDocument("$unwind",
new BsonDocument("path", "$Tickets")),
new BsonDocument("$group",
new BsonDocument
{
{ "_id", "$Tickets.Price" },
{ "Count", new BsonDocument("$sum", 1) }
})
};
var results = await _airplanesCollection.Aggregate(pipeline).ToListAsync();
var pricesCounts = new Dictionary<int, int>();
foreach (var result in results)
{
pricesCounts.Add(result.AsBsonDocument[0].ToInt32(), result.AsBsonDocument[1].ToInt32());
}
return pricesCounts;
It works exactly as I want it, however when I tried several ways to create a map-reduce function, it either threw exceptions or returned no results
/*string map = #"
function() {
var ticket = this;
emit(ticket.Price, { count: 1 });
}";
string reduce = #"
function(key, values) {
var result = {count: 0};
values.forEach(function(value){
result.count += value.count;
});
return result;
}";*/
string map = #"
function() {
for(var i = 0; i < this.Tickets.lenght; i++){
emit( this.Tickets[i].Price, { count: 1} );
}
}";
string reduce = #"
function(key, values) {
reducedVal = { count: 0};
for (var idx = 0; idx < values.length; idx++) {
reducedVal.count += values[idx].count;
}
return reducedVal;
}";
var options = new MapReduceOptions<Airplane, KeyValuePair<int, int>>();
options.OutputOptions = MapReduceOutputOptions.Inline;
var results = _airplanesCollection.MapReduceAsync(map, reduce, options).Result.ToList();
var pricesCounts = new Dictionary<int, int>();
foreach (var result in results)
{
pricesCounts.Add(result.Key, result.Value);
}
return pricesCounts;
The commented-out code throws this exception
The non commented-out code returns empty list.
The result of aggregation pipeline and wanted output from map-reduce
This is how I give it to controller of swagger
[ProducesResponseType(typeof(List<string>), 200)]
[ProducesResponseType(typeof(BadRequestObjectResult), 400)]
[HttpGet("groupTickets")]
public async Task<List<string>> GroupTickets()
{
var groupedTickets = await _mongoService.GroupTickets();
var result = new List<string>();
foreach(var item in groupedTickets)
{
result.Add($"Price: {item.Key} Bought tickets: {item.Value}");
}
return result.Any() ? result : new List<string>() { $"No tickets were bought yet." };
}
This is how my Airplane and Ticket models look like
public class Airplane
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Name { get; set; }
public int Capacity { get; set; }
public DateTime? FlightTime { get; set; }
public string Destination { get; set; }
public IEnumerable<Ticket> Tickets { get; set; }
public Airplane()
{
Tickets = new List<Ticket>();
}
}
public class Ticket
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public int TicketClass { get; set; }
public int Price { get; set; }
public string Destination { get; set; }
public string PassengerId { get; set; }
public Ticket() { Id = ObjectId.GenerateNewId().ToString(); }
}
Thank you for any help in advance.

How to use linq to "flatten" an hierachy?

Given the following code example, how do I:
Get the commented out lines in the unfiltered list to work (without changing the definition of Result)?
Get the commented out lines in the filtered list to work (without changing the definition of Result)? From my maths it should give 32 records. Hopefully my output intent is clear enough for others to understand
Any questions feel free to ask
Regards
Kyle
//Populate data
var alphas = new List<Alpha>();
for (int a = 1; a <= 10; a++)
{
var alpha = new Alpha() { Id = a, Name = "A" + a };
for (int b = 1; b <= 10; b++)
{
var beta = new Beta() { Id = b, Name = "B" + b };
for (int c = 1; c <= 10; c++)
{
var charlie = new Charlie() { Id = c, Name = "C" + c };
for (int d = 1; d <= 10; d++)
{
var delta = new Delta() { Id = d, Name = "D" + d };
charlie.Deltas.Add(delta);
}
beta.Charlies.Add(charlie);
}
alpha.Betas.Add(beta);
}
alphas.Add(alpha);
}
//Get results into required format without filtering
var unfilteredResults = alphas.Select(a => new Result
{
AId = a.Id,
AName = a.Name,
//BId = a.Betas.Select(b => b.Id),
//BName = a.Betas.Select(b => b.Name),
//CId = a.Betas.Select(b => b.Charlies.Select(c => c.Id)),
//CName = a.Betas.Select(b => b.Charlies.Select(c => c.Name)),
//DId = a.Betas.Select(b => b.Charlies.Select(c => c.Deltas.Select(d => d.Id))),
//DName = a.Betas.Select(b => b.Charlies.Select(c => c.Deltas.Select(d => d.Name)))
});
var whiteListAIds = new List<int>() { 1, 2 };
var whiteListBIds = new List<int>() { 3, 4 };
var whiteListCIds = new List<int>() { 5, 6 };
var whiteListDIds = new List<int>() { 7, 8 };
//Get results into required format with filtering
var filteredResults = alphas.Where(a => whiteListAIds.Contains(a.Id)).Select(a => new Result
{
AId = a.Id,
AName = a.Name,
//BId = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Id),
//BName = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Name),
//CId = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Charlies.Where(c => whiteListCIds.Contains(c.Id)).Select(c => c.Id)),
//CName = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Charlies.Where(c => whiteListCIds.Contains(c.Id)).Select(c => c.Name)),
//DId = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Charlies.Where(c => whiteListCIds.Contains(c.Id)).Select(c => c.Deltas.Where(d => whiteListDIds.Contains(d.Id)).Select(d => d.Id))),
//DName = a.Betas.Where(b => whiteListBIds.Contains(b.Id)).Select(b => b.Charlies.Where(c => whiteListCIds.Contains(c.Id)).Select(c => c.Deltas.Where(d => whiteListDIds.Contains(d.Id)).Select(d => d.Name)))
});
class Alpha
{
public int Id { get; set; }
public string Name { get; set; }
public List<Beta> Betas { get; set; } = new List<Beta>();
}
class Beta
{
public int Id { get; set; }
public string Name { get; set; }
public List<Charlie> Charlies { get; set; } = new List<Charlie>();
}
class Charlie
{
public int Id { get; set; }
public string Name { get; set; }
public List<Delta> Deltas { get; set; } = new List<Delta>();
}
class Delta
{
public int Id { get; set; }
public string Name { get; set; }
}
class Result
{
public int AId { get; set; }
public string AName { get; set; }
public int BId { get; set; }
public string BName { get; set; }
public int CId { get; set; }
public string CName { get; set; }
public int DId { get; set; }
public string DName { get; set; }
}
Got it working as below thanks to the first answer linq selectmany flatten multiple levels
Basically had to combine .SelectMany() on the "outer" parents and .Select() on the last/inner child.
//Populate data
var alphas = new List<Alpha>();
for (int a = 1; a <= 10; a++)
{
var alpha = new Alpha() { Id = a, Name = "A" + a };
for (int b = 1; b <= 10; b++)
{
var beta = new Beta() { Id = b, Name = "B" + b };
for (int c = 1; c <= 10; c++)
{
var charlie = new Charlie() { Id = c, Name = "C" + c };
for (int d = 1; d <= 10; d++)
{
var delta = new Delta() { Id = d, Name = "D" + d };
charlie.Deltas.Add(delta);
}
beta.Charlies.Add(charlie);
}
alpha.Betas.Add(beta);
}
alphas.Add(alpha);
}
var unfilteredResults = alphas.SelectMany(a => a.Betas.SelectMany(b=> b.Charlies.SelectMany(c=> c.Deltas.Select(d => new Result
{
AId = a.Id,
AName = a.Name,
BId = b.Id,
BName = b.Name,
CId = c.Id,
CName = c.Name,
DId = d.Id,
DName = d.Name
}))));
var whiteListAIds = new List<int>() { 1, 2 };
var whiteListBIds = new List<int>() { 3, 4 };
var whiteListCIds = new List<int>() { 5, 6 };
var whiteListDIds = new List<int>() { 7, 8 };
//Get results into required format with filtering
var filteredResults = unfilteredResults.Where(r => whiteListAIds.Contains(r.AId) && whiteListBIds.Contains(r.BId) && whiteListCIds.Contains(r.CId) && whiteListDIds.Contains(r.DId));
Console.WriteLine("Finished");
class Alpha
{
public int Id { get; set; }
public string Name { get; set; }
public List<Beta> Betas { get; set; } = new List<Beta>();
}
class Beta
{
public int Id { get; set; }
public string Name { get; set; }
public List<Charlie> Charlies { get; set; } = new List<Charlie>();
}
class Charlie
{
public int Id { get; set; }
public string Name { get; set; }
public List<Delta> Deltas { get; set; } = new List<Delta>();
}
class Delta
{
public int Id { get; set; }
public string Name { get; set; }
}
class Result
{
public int AId { get; set; }
public string AName { get; set; }
public int BId { get; set; }
public string BName { get; set; }
public int CId { get; set; }
public string CName { get; set; }
public int DId { get; set; }
public string DName { get; set; }
}

Optimization of custom rank function in C#

I have written a code for students class to Rank them according to their marks. The code works accurately and gives the result as
Name:B Marks:30 Rank:1
Name:C Marks:30 Rank:1
Name:A Marks:20 Rank:3
Name:D Marks:10 Rank:4
But I need it to be optimized so it wont take too much of time to be processed. Below is the code
public List<Students> GetRanks(List<Students> students)
{
List<Students> rStudents = new List<Students>();
students = students.OrderByDescending(a => a.marks).ToList();
for (int i = 0; i < students.Count; i++)
{
Students stu = new Students();
stu.Id = students[i].Id;
stu.Name = students[i].Name;
stu.marks = students[i].marks;
if (i > 0 && students[i].marks == students[i - 1].marks)
{
stu.rank = rStudents.Select(a => a.rank).LastOrDefault();
}
else
{
stu.rank = i + 1;
}
rStudents.Add(stu);
}
return rStudents;
}
List<Students> students = new List<Students>() {
new Students() { Id = 1, Name = "A", marks = 20 },
new Students() { Id = 1, Name = "B", marks = 30 },
new Students() { Id = 1, Name = "C", marks = 30 },
new Students() { Id = 1, Name = "D", marks = 10 },
};
List<Students> rStudents = GetRanks(students);
public class Students
{
public int Id { get; set; }
public string Name { get; set; }
public double marks { get; set; }
public int rank { get; set; }
}
foreach (Students s in GetRanks(students))
{
Console.WriteLine($"Name:{s.Name}\tMarks:{s.marks}\tRank:{s.rank}");
}
Remove unnecessary resource.and using yield will have performance impact in large data. Fewer line and more readable.
If you are fetching data from DataBase it's better idea sort them there then fetch them.
public static IEnumerable<Students> GetRanks(List<Students> students)
{
List<Students> rStudents = students.OrderByDescending(a => a.marks).ToList();
for (int i = 0; i < rStudents.Count; i++)
{
if (i > 0 && rStudents[i].marks == rStudents[i - 1].marks)
{
rStudents[i].rank = rStudents[i - 1].rank;
}
else
{
rStudents[i].rank = i + 1;
}
yield return rStudents[i];
}
}
This should be pretty darn fast:
var rank = 1;
students
.GroupBy(x => x.marks)
.OrderByDescending(x => x.Key)
.ToList()
.ForEach(xs =>
{
xs.ToList().ForEach(x => x.rank = rank);
rank += xs.Count();
});
With your sample data I get:
Here's a non-destructive version of the code:
public static List<Students> GetRanks(List<Students> students)
{
var rank = 1;
return
students
.OrderByDescending(x => x.marks)
.GroupBy(x => x.marks)
.SelectMany(xs =>
{
var r = rank;
rank += xs.Count();
return xs.Select(x => new Students()
{
Id = x.Id,
Name = x.Name,
marks = x.marks,
rank = r,
});
})
.ToList();
}
I measured this using 100_000 randomly created students. My code completed in 110 milliseconds. The original code in the question took 64_119 milliseconds.
Yes, this code was nearly 600x faster.
More then 50% improvement if have large collect with a new method called GetRanksV1
namespace StudentPerformance
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
List<Students> students = new List<Students>();
Random _random = new Random();
for (int i = 1; i < 10001; i++)
{
students.Add(new Students() { Id = i, Name = "A" + i, marks = _random.Next(10, 50) });
}
var watch = System.Diagnostics.Stopwatch.StartNew();
var tmp = GetRanks(students);
watch.Stop();
Console.WriteLine($"Execution Time: {watch.ElapsedMilliseconds} ms");
watch = System.Diagnostics.Stopwatch.StartNew();
var tmp1 = GetRanksV1(students);
watch.Stop();
Console.WriteLine($"Execution Time V1: {watch.ElapsedMilliseconds} ms");
foreach (Students s in tmp1)
{
Console.WriteLine($"Name:{s.Name}\tMarks:{s.marks}\tRank:{s.rank}");
}
Console.ReadLine();
}
public static List<Students> GetRanks(List<Students> students)
{
List<Students> rStudents = new List<Students>();
students = students.OrderByDescending(a => a.marks).ToList();
for (int i = 0; i < students.Count; i++)
{
Students stu = new Students();
stu.Id = students[i].Id;
stu.Name = students[i].Name;
stu.marks = students[i].marks;
if (i > 0 && students[i].marks == students[i - 1].marks)
{
stu.rank = rStudents.Select(a => a.rank).LastOrDefault();
}
else
{
stu.rank = i + 1;
}
rStudents.Add(stu);
}
return rStudents;
}
public static List<Students> GetRanksV1(List<Students> students)
{
int Srno = 0;
var tmp = students.GroupBy(a => a.marks).OrderByDescending(o => o.Key).Select(s => new { Mark = s.Key, Rand = ++Srno, });
var values = from s in students
join t in tmp on s.marks equals t.Mark
select new Students { Id = s.Id, Name = s.Name, marks = s.marks, rank = t.Rand };
return values.ToList();
}
}
public class Students
{
public int Id { get; set; }
public string Name { get; set; }
public double marks { get; set; }
public int rank { get; set; }
}
}

Is there a way to Translate an array into a dictionary

The problem I'm having is that I'm not sure how to translate my double array into a Dictionary. I have some LINQ Where statements that are pretty heavy, so I wanted to use the keys of my Dictionary to look up a specific value. I have two classes with getters and setters and then my calculator.
I've fiddled around a bit with trying to make either a lookup or a Dictionary, but didn't have any luck.
public class Product
{
public int EarliestOriginYear { get; set; }
public int NoOfDevelopmentYears { get; set; }
public string ProductName { get; set; }
public IEnumerable<ProductIncrementalValue> ProductIncrementalValues { get; set; }
}
public class ProductIncrementalValue
{
public string ProductName { get; set; }
public int OriginYear { get; set; }
public int DevelopmentYear { get; set; }
public double IncrementalValue { get; set; }
}
public IList<double> Calculate(Product product)
{
IList<double> cumulativeDataTriangle = new List<double>();
if (!product.ProductIncrementalValues.Any())
return cumulativeDataTriangle;
for (int i = 0; i < product.NoOfDevelopmentYears; i++)
{
// This is what I want to change (where statements)
var incrementalValues = product.ProductIncrementalValues
.Where(v => v.OriginYear == product.EarliestOriginYear + i)
.ToList();
double previous = 0;
for (int j = 0; j < product.NoOfDevelopmentYears - i; j++)
{
// This is what I want to change
double incrementalValue = incrementalValues.Where(val =>
val.DevelopmentYear == val.OriginYear + j)
.Select(x => x.IncrementalValue)
.FirstOrDefault();
var tmp = incrementalValue + previous;
cumulativeDataTriangle.Add(tmp);
previous = tmp;
}
}
return cumulativeDataTriangle;
}
You could group the products by OriginYear and DevelopmentYear before the loops. Something like this might help:
public IList<double> Calculate(Product product)
{
IList<double> cumulativeDataTriangle = new List<double>();
if (!product.ProductIncrementalValues.Any())
return cumulativeDataTriangle;
var lookup = product.ProductIncrementalValues.ToLookup(v => (v.OriginYear, v.DevelopmentYear), v => v.IncrementalValue);
for (int i = 0; i < product.NoOfDevelopmentYears; i++)
{
var originYear = product.EarliestOriginYear + i;
double previous = 0;
for (int j = 0; j < product.NoOfDevelopmentYears - i; j++)
{
var developmentYear = originYear + j;
var incrementalValues = lookup[(originYear, developmentYear)];
double incrementalValue = incrementalValues.FirstOrDefault();
var tmp = incrementalValue + previous;
cumulativeDataTriangle.Add(tmp);
previous = tmp;
}
}
return cumulativeDataTriangle;
}
Thanks to the previous answer, I managed to implement a Dictionary based on OriginYear and DevelopmentYear, which returns an IncrementalValue.
public IList<double> Calculate(Product product)
{
IList<double> cumulativeDataTriangle = new List<double>();
if (!product.ProductIncrementalValues.Any())
return cumulativeDataTriangle;
var lookup = product.ProductIncrementalValues.
ToDictionary(v => (v.OriginYear, v.DevelopmentYear), v => v.IncrementalValue);
for (int i = 0; i < product.NoOfDevelopmentYears; i++)
{
var originYear = product.EarliestOriginYear + i;
double previous = 0;
for (int j = 0; j < product.NoOfDevelopmentYears - i; j++)
{
var developmentYear = originYear + j;
double incrementalValue;
lookup.TryGetValue((originYear, developmentYear), out incrementalValue);
var tmp = incrementalValue + previous;
cumulativeDataTriangle.Add(tmp);
previous = tmp;
}
}
return cumulativeDataTriangle;
}

Time of query very long

i'm new on the world of elasticsearch and i'm trying to code it in c# with the NEST aPI.
I succed to index some doc with content but when i try to search, the research take ~4sec.
I use visual studio 2012
I hope you can help me :)
[ElasticType(Name = "document")]
public class Document
{
public int Id { get; set; }
[ElasticProperty(Store = true)]
public string Titre { get; set; }
[ElasticProperty(Type = FieldType.Attachment, TermVector = TermVectorOption.WithPositionsOffsets, Store = true)]
public Attachment File { get; set; }
}
public class Attachment
{
[ElasticProperty(Name = "_content")]
public string Content { get; set; }
[ElasticProperty(Name = "_content_type")]
public string ContentType { get; set; }
[ElasticProperty(Name = "_name")]
public string Name { get; set; }
}
static int i = 0;
static int j = 0;
This is my class's declarations
static void Main(string[] args)
{
//New connection
//var node = new Uri("http://serv-intra:9200");
var node = new Uri("http://localhost:9200/");
var settings = new ConnectionSettings(
node,
defaultIndex: "document"
);
var client = new ElasticClient(settings);
//Creation of my index with mapping
//client.CreateIndex("document", c => c
// .AddMapping<Document>(m => m.MapFromAttributes())
// );
//function for index
//feignasse(client);
var query = Query<Document>.Term("_all", "chu");
var searchResults = client.Search<Document>(s => s
.From(0)
.Size(200)
.Query(query)
);
}
protected static void feignasse(ElasticClient client)
{
// Create new stopwatch
Stopwatch stopwatch = new Stopwatch();
// Begin timing
stopwatch.Start();
Indexation(#"\\serv-intra\Documents", client);
// Stop timing
stopwatch.Stop();
}
//This is my function for index
protected static void Indexation(string path, ElasticClient client)
{
string[] rootDirectories = Directory.GetDirectories(path);
string[] rootFiles = Directory.GetFiles(path);
foreach (string nomfich in rootFiles)
{
if (nomfich.Length < 256)
{
FileInfo file = new FileInfo(nomfich);
var attachement = new Attachment();
attachement.Content = Convert.ToBase64String(File.ReadAllBytes(nomfich));
attachement.Name = file.Name;
attachement.ContentType = GetMimeType(file.Extension);
var document = new Document
{
Id = i,
Titre = file.Name,
File = attachement,
};
var index = client.Index(document);
i++;
}
else
{
j++;
}
}
foreach (string newPath in rootDirectories)
{
Indexation(newPath, client);
}
So i explain you, in my server i have a sharing of doc, and i just travel him in order to catch all my doc and index then in elasticsearch
I have to node with 0 replica and 5 shards
Thanks you

Categories

Resources