Create dynamic pivot list using joins - c#

I want to create dynamic pivot list on list which has data in below format
"products" :
{
"name": "ABC",
"Variance": [
{
"Date": "01-01-2018",
"Value": "10"
},
{
"Date": "02-01-2018",
"Value": "11"
},
{
"Date": "03-01-2018",
"Value": "12"
},
]
},
{
"name": "XYZ",
"Variance": [
{
"Date": "01-01-2018",
"Value": "22"
},
{
"Date": "03-01-2018",
"Value": "24"
},
{
"Date": "04-01-2018",
"Value": "28"
},
],
},
{
"name": "PQR",
"Variance": [
{
"Date": "01-01-2018",
"Value": "20"
},
{
"Date": "02-01-2018",
"Value": "22"
},
{
"Date": "04-01-2018",
"Value": "24"
},
],
}
I want to create pivot list so it can return data like
"NewProducts":[{
"Date": "01-01-2018",
"ABC" : "10"
"XYZ" : "22",
"PQR" : "20"
},
{
"Date": "02-01-2018",
"ABC" : "11"
"XYZ" : "null",
"PQR" : "22"
},
{
"Date": "03-01-2018",
"ABC" : "12"
"XYZ" : "24",
"PQR" : "null"
},
{
"Date": "04-01-2018",
"ABC" : "null"
"XYZ" : "28",
"PQR" : "24"
}]
I tried some approach of having joins on inner lists, but not getting required results. I want to avoid loops as my product list will vary as per selections.
I was able to join the list using for loops, but I want to have minimal use of for loops. Any suggestions would be really helpful to me.
Thanks in Advance.

Assuming you want to use a Dictionary<string,int> to hold the dynamic value pairs, you can use LINQ by first flattening the nested structure into a new flat list with SelectMany and then grouping by Date:
var ans = products.SelectMany(p => p.Variance.Select(v => new { p.name, v.Date, v.Value }))
.GroupBy(pv => pv.Date)
.Select(pvg => new { Date = pvg.Key, Fields = pvg.ToDictionary(p => p.name, p => p.Value) });

Related

LINQ. ordered list by key group but first group are with condition [duplicate]

This question already has answers here:
Linq Order by a specific number first then show all rest in order
(6 answers)
Closed yesterday.
I"m have List ordered by key group.
var productList = CustomerData.Data.Products
.GroupBy(x => x.CategoryDesc)
.OrderBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.ToList());
it's good working
And i want one SPECIFIC group (with sale) set as first in the list.
But the rest of the groups to stay sorted in descending order.
something like that:
***I wrote in the format for the convenience of seeing
[
{
"Key": "SPECIFIC",
"Value": [
{
"SubGroup": "wire cutters",
"Model": "A",
"Price": "65"
},
{
"SubGroup": "hammers",
"Model": "B",
"Price": "71"
},
{
"SubGroup": "hammers",
"Model": "C",
"Price": "92.5"
},
]
},
{
"Key": "Rank A",
"Value": [
{
"SubGroup": "hammers",
"Model": "A",
"Price": "130"
},
{
"SubGroup": "hammers",
"Model": "B",
"Price": "142"
},
{
"SubGroup": "hammers",
"Model": "C",
"Price": "185"
},
]
},
{
"Key": "Rank B",
"Value": [
{
"SubGroup": "pliers",
"Model": "A",
"Price": "95"
},
{
"SubGroup": "pliers",
"Model": "B",
"Price": "59"
},
{
"SubGroup": "pliers",
"Model": "C",
"Price": "65"
},
]
},
{
"Key": "Rank C",
"Value": [
{
"SubGroup": "saws",
"Model": "A",
"Price": "905"
},
{
"SubGroup": "saws",
"Model": "B",
"Price": "589"
},
{
"SubGroup": "saws",
"Model": "C",
"Price": "655"
},
]
},
]
Add additional Order. First for detecting specific items, ThenBy for the rest.
var productList = CustomerData.Data.Products
.GroupBy(x => x.CategoryDesc)
.OrderByDescending(x => x.Key == "SPECIFIC")
.ThenBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.ToList());

Retrieving list of documents from collection by id in nested list

I have documents like this:
[
// 1
{
"_id": ObjectId("573f3944a75c951d4d6aa65e"),
"Source": "IGN",
"Family": [
{
"Countries": [
{
"uid": 17,
"name": "Japan",
}
]
}
]
},
// 2
{
"_id": ObjectId("573f3d41a75c951d4d6aa65f"),
"Source": "VG",
"Family": [
{
"Countries": [
{
"uid": 17,
"name": "USA"
}
]
}
]
},
// 3
{
"_id": ObjectId("573f4367a75c951d4d6aa660"),
"Source": "NRK",
"Family": [
{
"Countries": [
{
"uid": 17,
"name": "Germany"
}
]
}
]
},
// 4
{
"_id": ObjectId("573f4571a75c951d4d6aa661"),
"Source": "VG",
"Family": [
{
"Countries": [
{
"uid": 10,
"name": "France"
}
]
}
]
},
// 5
{
"_id": ObjectId("573f468da75c951d4d6aa662"),
"Source": "IGN",
"Family": [
{
"Countries": [
{
"uid": 14,
"name": "England"
}
]
}
]
}
]
I want to return only the documents with source equals 'Countries.uid' equal 17
so I have in the end :
[
{
"_id": ObjectId("573f3944a75c951d4d6aa65e"),
"Source": "IGN",
"Family": [
{
"Countries": [
{
"uid": 17,
"name": "Japan",
}
]
}
]
},
{
"_id": ObjectId("573f3d41a75c951d4d6aa65f"),
"Source": "VG",
"Family": [
{
"Countries": [
{
"uid": 17,
"name": "USA"
}
]
}
]
},
{
"_id": ObjectId("573f4367a75c951d4d6aa660"),
"Source": "NRK",
"Family": [
{
"Countries": [
{
"uid": 17,
"name": "Germany"
}
]
}
]
}
]
How can I do this with the official C# MongoDB driver?
Tried this :
public List<Example> getLinkedCountry(string porduitId)
{
var filter = Builders<Example>.Filter.AnyIn("Family.Countries.uid", porduitId);
var cursor = await _certificats.FindAsync(filter);
var docs = cursor.ToList();
return docs;
}
Unfortunately, I think my filter is wrong.
Is there a way to find all the documents by accessing the nested list by id and retrieving it?
Solution 1
Use ElemMatch instead of AnyIn.
var filter = Builders<Example>.Filter.ElemMatch(
x => x.Family,
y => y.Countries.Any(z => z.uid == porduitId));
Output
Solution 2
If you are unconfident with MongoDB .Net Driver syntax, you can convert the query as BsonDocument via MongoDB Compass (Export to language feature).
var filter = new BsonDocument("Family.Countries.uid", porduitId);
Just to expand on #Yong Shun 's answer,
if you just want to return the list of nested documents and not all of it, you have a few options.
Using project
var filter = Builders<Example>.Filter.ElemMatch(
x => x.Family,
y => y.Countries.Any(z => z.uid == porduitId));
var project = Builders<Example>.Project.ElemMatch(
x => x.Family,
y => y.Countries.Any(z => z.uid == porduitId)
);
var examples = await collection.filter(filter).Project<Example>(project).toListAsync();
Using the aggregate pipeline
var filter = Builders<Example>.Filter.ElemMatch(
x => x.Family,
y => y.Countries.Any(z => z.uid == porduitId));
var project = Builders<ServiceProvider>.Projection.Expression(
x => x.Faimily.Where(y => y.uid == porduitId)
);
var result = await collection
.Aggregate()
.Match(filter)
.Project(project)
.ToListAsync(); //Here result is a list of Iterable<Countries>

How to compare single property from 2 array using c# execution time should be minimum

I have two arrays variables and values like below
arraydata1 =
[
{
"id": "1",
"name": "aaa"
},
{
"id": "2",
"name": "bbb"
},
{
"id": "3",
"name": "ccc"
},
{
"id": "4",
"name": "ddd"
},
{
"id": "12",
"name": "aaa"
}
]
and
arraydata2 =
[
{
"id": "111",
"tablename": "aaa"
},
{
"id": "222",
"tablename": "bbb"
}
]
I want to compare arraydata1.name == arraydata2.tablename and if matching then form new array from arraydata1 .
output is -
[
{
"id": "1",
"name": "aaa"
},
{
"id": "2",
"name": "bbb"
},
{
"id": "12",
"name": "aaa"
}
]
I have more than 2000+ records to compare in arraydata1 how to reduce time as well. I can use normal foreach but it will take too much time to compare.
I was doing inside logic app using 2 foreach so it is taking time. so i thought better to use c# code.
One Linq solution could look like this:
var tableNameKeys = arraydata2.Select(t => t.tablename).ToHashSet();
var resultArray = arraydata1.Where(x => tableNameKeys.Contains(x.name)).ToArray();
The advantage of this approach is that HashSet.Contains
... is an O(1) operation.
Result:

Zip collections matching by ID

This is a repost of a previous question that was wrongly closed as a duplicate:
Say I have two collections of the same type similar to the following:
OldCollection: [{ID: 1, other props}, {ID: 2, other props}, {ID: 3, other props}]
NewCollection: [{ID: 1, other props}, {ID: 3, other props}, {ID: 4, other props}]
Is there a way to zip the collections matching ID's to get a Tuple result like the following:
[{OLD-ID1, NEW-ID1},
{OLD-ID2, null},
{OLD-ID3, NEW-ID3},
{null, NEW-ID4}]
So basically I want to zip the collections together matching on ID and if only one collection has an entry with a particular ID the Tuple should fill in null for that spot.
IMPORTANT: This is not a duplicate of the full outer join solution. I do not want to combine my results so that Col1-ID1 gets merged with Col2-ID1. I Just want to Tuple them so I can see them side by side. Think of it as Collection 1 is the old values and Collection 2 is the new values. I want them to be paired up into Tuples so I can see ID1 and ID3 were updated, ID2 was removed, and ID4 was added.
Here is a DotNetFiddle example that almost does what I want.
The results of that fiddle is:
[
{
"Item1": {
"id": 1,
"name": "firstOld"
},
"Item2": {
"id": 1,
"name": "firstNew"
}
},
{
"Item1": {
"id": 2,
"name": "secondOld"
},
"Item2": {
"id": 3,
"name": "thirdNew"
}
},
{
"Item1": {
"id": 3,
"name": "thirdOld"
},
"Item2": {
"id": 4,
"name": "fourthNew"
}
},
{
"Item1": null,
"Item2": {
"id": 5,
"name": "fifthNew"
}
}
]
What I want is this:
[
{
"Item1": {
"id": 1,
"name": "firstOld"
},
"Item2": {
"id": 1,
"name": "firstNew"
}
},
{
"Item1": {
"id": 2,
"name": "secondOld"
},
"Item2": null
},
{
"Item1": {
"id": 3,
"name": "thirdOld"
},
"Item2": {
"id": 3,
"name": "thirdNew"
}
},
{
"Item1": null,
"Item2": {
"id": 4,
"name": "fourthNew"
}
},
{
"Item1": null,
"Item2": {
"id": 5,
"name": "fifthNew"
}
}
]
You want a full-outer-join anyway, but you could use this easy approach:
var oldByID = oldCollection.ToDictionary(x => x.Id);
var newByID = newCollection.ToDictionary(x => x.Id);
IEnumerable<int> allIds = oldByID.Keys.Union(newByID.Keys);
var oldAndNew = allIds.Select(id =>
(Old: oldByID.TryGetValue(id, out var oldObj) ? oldObj : null,
New: newByID.TryGetValue(id, out var newObj) ? newObj : null));
So first create two dictionaries to lookup the old and new objects via Id efficiently. Then collect all Id's and use Select to get the old/new object with each Id. If it's not available in the dictionary it will assign null to the tuple item.

Find field with specific key in Json

In my c# project I use Json.net Library.
I have long Json with many subfields, for ex:
{
"count": 10,
"Foo1": [
{
"id": "1",
"name": "Name1"
},
{
"id": "2",
"name": "Name3"
},
{
"id": "3",
"name": "Name4"
}
],
"Foo2": [
{
"id": "4",
"name": "Name3",
"specific_field": "specific_values1"
},
{
"id": "5",
"name": "Name3",
"specific_field": "specific_values2"
},
{
"id": "6",
"name": "Name3",
"specific_field": "specific_values3"
}
],
"Foo3": [
{
"id": "7"
},
{
"id": "8"
},
{
"id": "9"
}
]
}
And I need to get List of all specific_field (id 4-6), but cant deserialized json to object, because Foo1, Foo2 ... changed dynamically.
I want to know, is this possible to get values of specific_field when i have only json?
I think, I found solution:
var list = new List<string>();
var result = ((JToken)json);
foreach (var res in result)
{
list.AddRange(from foo in res.First let ret = foo["specific_field"] where (dynamic) ret != null select foo["specific_field"].ToString());
}
In comment, provide, what do you think about it?
You could use dynamics:
string json = "your JSON string comes here";
dynamic deserializedValue = JsonConvert.DeserializeObject(json);
var values = deserializedValue["Foo2"];
for (int i = 0; i < values.Count; i++)
{
Console.WriteLine(values[i]["specific_field"]);
}

Categories