Generate recursive object list in C# - c#

I have class models like below :
public class ABC
{
public string value { get; set; }
public List<ABC> children { get; set; }
}
And structure data like this
Parent Value
1 A
A C
2 B
B D
I would like to recursively build an complex object. I have managed to recursively add Children to children.
How can I return List<ABC> like results below ?
[
{
value: '1',
children: [
{
value: 'A',
children: [
{
value: 'C',
},
],
},
],
},
{
value: '2',
children: [
{
value: 'B',
children: [
{
value: 'D',
},
],
},
],
},
];

This sample output what you expect, with more children.
using System;
using System.Collections.Generic;
using System.Text.Json;
public class Json
{
public class ABC
{
public string value { get; set; }
public List<ABC> children { get; set; }
}
public void Run()
{
var abcs = new List<ABC>();
abcs.Add(new ABC()
{
value = "1",
children = new List<ABC>()
{
new ABC() { value = "A", children = new List<ABC>() {
new ABC() { value = "C" }
}},
new ABC() { value = "F" }
}
});
abcs.Add(new ABC()
{
value = "2",
children = new List<ABC>()
{
new ABC() { value = "B", children = new List<ABC>() {
new ABC() { value = "D" }
}},
new ABC() { value = "G" }
}
});
var ser = JsonSerializer.Serialize(abcs, new JsonSerializerOptions() { WriteIndented = true, DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull });
Console.WriteLine(ser);
}
}

Related

SelectMany on a list of objects

I have a class:
public class User
{
public Guid ObjectId { get; set; }
public List<Guid> Groups { get; set; }
}
public class Group
{
public List<User> Users { get; set; }
}
Example:
Users:
[
{
"ObjectId": "1",
"Groups": ["G1"]
},
{
"ObjectId": "2",
"Groups": ["G2"]
},
{
"ObjectId": "3",
"Groups": ["G3"]
},
{
"ObjectId": "1",
"Groups": ["G4"]
}
]
Note that ObjectId "1" is present 2 times (One with G1, One with G4)
On running
var source = Group.SelectMany(x => x.Users).ToList();
I see the output as:
[
{
"ObjectId": "1",
"Groups": ["G1"]
},
{
"ObjectId": "2",
"Groups": ["G2"]
},
{
"ObjectId": "3",
"Groups": ["G3"]
}
]
How do I get the output as:
[
{
"ObjectId": "1",
"Groups": ["G1"]
},
{
"ObjectId": "2",
"Groups": ["G2"]
},
{
"ObjectId": "3",
"Groups": ["G3"]
},
{
"ObjectId": "1",
"Groups": ["G4"]
}
]
If the input is:
Users:
[
{
"ObjectId": "1",
"Groups": ["G1"]
},
{
"ObjectId": "2",
"Groups": ["G2"]
},
{
"ObjectId": "3",
"Groups": ["G3"]
},
{
"ObjectId": "1",
"Groups": ["G1"]
}
]
The output should be:
[
{
"ObjectId": "1",
"Groups": ["G1"]
},
{
"ObjectId": "2",
"Groups": ["G2"]
},
{
"ObjectId": "3",
"Groups": ["G3"]
}
]
UPDATE:
Apologize as my question was not clear:
Classes:
public class GroupMembership
{
public List<AzureADUser> SourceMembers { get; set; }
}
public class AzureADUser
{
public Guid ObjectId { get; set; }
public List<Guid> SourceGroups { get; set; }
}
var users1 = new List<AzureADUser> {
new () { ObjectId = new Guid("Guid1"), SourceGroups = new List<Guid> {new Guid("GuidG1")}},
new () { ObjectId = new Guid("Guid2"), SourceGroups = new List<Guid> {new Guid("GuidG2")}},
new () { ObjectId = new Guid("Guid3"), SourceGroups = new List<Guid> {new Guid("GuidG3")}},
new () { ObjectId = new Guid("Guid1"), SourceGroups = new List<Guid> {new Guid("GuidG4")}} //include this
};
var users2 = new List<AzureADUser> {
new () { ObjectId = new Guid("Guid1"), SourceGroups = new List<Guid> {<GuidG1>}} // remove this as this is a duplicate
};
var groupMembership1 = new GroupMembership
{
SourceMembers = users1;
};
var groupMembership2 = new GroupMembership
{
SourceMembers = users2;
};
var groupsMemberships = new List<GroupMembership>();
groupsMemberships.Add(groupMembership1);
groupsMemberships.Add(groupMembership2);
/* output:
ObjectId: new Guid("Guid1"), SourceGroups: new Guid("Guid1")
ObjectId: new Guid("Guid2"), SourceGroups: new Guid("Guid2")
ObjectId: new Guid("Guid3"), SourceGroups: new Guid("Guid3")
ObjectId: new Guid("Guid1"), SourceGroups: new Guid("GuidG4")
*/
SelectMany isn't the problematique API here, it just collects the existing sub-collections of Users into one. It's doesn't remove any and doesn't look at the data at all.
If your goal is to remove duplicates from your list, you could use LINQ's Distinct method on the result of SelectMany - and provide the appropriate IEqualityComparer that checks the User data for equaility. The comparison should return true if all values, ObjectID and all elements in Group, are equal.
Your code is somewhat confusing: Your JSON deals with ints and strings, but the class defs uses GUIDs. I'll use the JSON definition for the following example:
public static void Main()
{
var users = new List<User> {
new () { ObjectId = 1, Groups = new () {"G1"}},
new () { ObjectId = 2, Groups = new () {"G2"}},
new () { ObjectId = 3, Groups = new () {"G3"}},
new () { ObjectId = 1, Groups = new () {"G4"}}, // should remain
new () { ObjectId = 1, Groups = new () {"G1"}}, // should be removed equal to 1st
};
var distinct = users.Distinct(new UserComp());
foreach (var usr in distinct)
Console.WriteLine(usr);
/* output:
ID: 1, Groups: G1
ID: 2, Groups: G2
ID: 3, Groups: G3
ID: 1, Groups: G4
*/
}
public class UserComp : IEqualityComparer<User> {
public bool Equals (User usr1, User usr2)
{
if (usr1 is null) return usr2 is null;
if (usr2 is null) return false;
return usr1.ObjectId == usr2.ObjectId && ((usr1.Groups is {} g1 && usr2.Groups is {} g2 && g1.SequenceEqual(g2)) || (usr1.Groups is null && usr2.Groups is null));
}
// hash is kind of weak ignoring the group data, but suitable for the demo
public int GetHashCode (User usr) => usr?.ObjectId ?? 0;
}
public class User
{
public int ObjectId { get; set; }
public List<string> Groups { get; set; }
public override string ToString () => $"ID: {ObjectId}, Groups: {(Groups == null ? "-" : string.Join(", " ,Groups))}";
}
Note that you don't need an explicit equaility comparer class if you put the same logic into the override of User.Equals.

Customize object serialization to Json in c#

I am making an asp.net core web api and i want to return Ok(JsonList) in a specific format
I have a list of the following object:
public class obj
{
string schoolName;
int studentscount;
int teacherscount;
}
that would be serialized by default to:
[{"schoolName":"name_1",
"studentscount" : "5",
"teacherscount" : "2"
},
{"schoolName":"name_2",
"studentscount" : "10",
"teacherscount" : "3"
}]
I want the name property to be the name of the object :
[{
"name_1":{
"studentscount" : "5",
"teacherscount" : "2"
},
"name_2:"{
"studentscount" : "10",
"teacherscount" : "3"
}
}]
you can create a new class and try this
Dictionary<string, Counts> counts = JArray.Parse(json).ToDictionary(j => (string)j["schoolName"], j => new Counts
{
studentscount = (int)j["studentscount"],
teacherscount = (int)j["teacherscount"]
});
json = JsonConvert.SerializeObject(counts, Newtonsoft.Json.Formatting.Indented);
public class Counts
{
public int studentscount { get; set; }
public int teacherscount { get; set; }
}
result
{
"name_1": {
"studentscount": 5,
"teacherscount": 2
},
"name_2": {
"studentscount": 10,
"teacherscount": 3
}
}
but if for some reasons you still need an array
var countsArray = new List<Dictionary<string,Counts>> {counts};
json=JsonConvert.SerializeObject(countsArray,Newtonsoft.Json.Formatting.Indented);
result
[
{
"name_1": {
"studentscount": 5,
"teacherscount": 2
},
"name_2": {
"studentscount": 10,
"teacherscount": 3
}
}
]

How to update all keys in MongoDB from a dictionary?

I have some object:
public class ObjA
{
[BsonElement("_id")]
public int ID { get; set; }
[BsonElement("languages")]
public Dictionary<string, ObjB> languages { get; set; }
}
public class ObjB
{
[BsonElement("translation_1")]
public string Translation_1 { get; set; }
[BsonElement("translation_2")]
public string Translation_2 { get; set; }
[BsonElement("translation_3")]
public string Translation_3 { get; set; }
}
There are situations where I need to update Translation_1 property of ObjB for every key in languages property of ObjA object.
Value of key of languages dictionary is not the same for all ObjA.
So, query should update all Translation_1 properties regardless of the key
Update:
So far I have not made any significant progress:
UpdateDefinition<ObjA> update = Builders<ObjA>.Update.Set("languages.-key-.translation_1", newValue);
var result = await Collection.UpdateManyAsync(x => x.Id == someID, update);
There are situations where I need to update Translation_1 property of
ObjB for every key in languages property of ObjA object.
Here is the Update with Aggregation Pipeline code, the first one is the mongo shell version and the second the C# version. The update modifies the value of the property translation_1 of ObjB, for all keys of the languages dictionary property of ObjA.
var NEW_VALUE = "some new value"
var someId = "some id value"
db.test.updateOne(
{ _id: someId },
[
{
$set: {
languages: {
$map: {
input: { $objectToArray: "$languages" },
as: "ele",
in: {
$mergeObjects: [
"$$ele",
{ "v": {
"translation_1": NEW_VALUE,
"translation_2": "$$ele.v.translation_2",
"translation_3": "$$ele.v.translation_3"
} } ]
}
}
}
}},
{
$set: {
languages: {
$arrayToObject: "$languages"
}
}},
]
)
var pipeline = new BsonDocumentStagePipelineDefinition<ObjA, ObjA>(
new[] {
new BsonDocument("$set",
new BsonDocument("languages",
new BsonDocument("$map",
new BsonDocument {
{ "input", new BsonDocument("$objectToArray", "$languages") },
{ "as", "ele" },
{ "in",
new BsonDocument("$mergeObjects",
new BsonArray {
"$$ele",
new BsonDocument("v",
new BsonDocument {
{ "translation_1", NEW_VALUE },
{ "translation_2", "$$ele.v.translation_2" },
{ "translation_3", "$$ele.v.translation_3" }
})
}) }
}
))),
new BsonDocument("$set",
new BsonDocument("languages",
new BsonDocument("$arrayToObject", "$languages")
))
}
);
System.Linq.Expressions.Expression<Func<ObjA, bool>> filter = x => x.id == someId;
var update = new PipelineUpdateDefinition<ObjA>(pipeline);
var result = collection.UpdateOne<ObjA>(filter, update);

How to combine two lists with different properties based on a condition in C#

i have two lists -plans and divisions with different properties ..
Sample data:
{
"plans": [
{
"planCode": "A",
"planShortName": "Apple",
"planType": null,
"bisPlanCode": "878",
},
{
"planCode": "B",
"planShortName": "Ball",
"planType": null,
"bisPlanCode": "536",
}
],
"divisions": [
{
"planCode": "878",
"divisions": [
{ divisionCode: "2", divisionName: "test2" },
{ divisionCode: "1", divisionName: "test1" }]
},
{
"planCode": "536",
"divisions": [
{ divisionCode: "3", divisionName: "test3" },
{ divisionCode: "1", divisionName: "test1" }
]
}
]
}
How to combine both these lists- plans and divsions into one list planDivisions on a condition where plans.bisPlanCode == divisions.plansCode in C#.
so the final result should look like
"planDivisions": [
{
"planCode": "A",
"planShortName": "Apple",
"planType": null,
"bisPlanCode": "878",
"divisions": [ { divisionCode: "2", divisionName: "test2" },
{ divisionCode: "1", divisionName: "test1" }]
},
{
"planCode": "B",
"planShortName": "Ball",
"planType": null,
"bisPlanCode": "536",
"divisions": [
{ divisionCode: "3", divisionName: "test3" },
{ divisionCode: "1", divisionName: "test1"
}]
}
]
What i have tried to do :
List<Data> planDivisions = new List<Data>();
Divisions.ForEach(division =>
{
var plan = Plans.Find(p => p.PlanCode == division.PlanCode);
if (plan != null)
{
Data data = new Data();
data.DivisionData = division;
data.PlanData = plan;
planDivisions.Add(data);
}
});
Is there any effecient way to do ?
You can use LINQ to join both data lists. More about it here.
class Plan {
public int PlanCode { get; set; }
public string PlanShortName { get; set; }
}
class Division {
public int DivisionCode { get; set; }
public string DivisionName { get; set; }
}
class DivisionAssignment {
public int PlanCode { get; set; }
public List<Division> Divisions { get; set; }
}
public static void JoinExample() {
// do your deserialization stuff here
List<Plan> plans = ...;
List<DivisionAssignment> assignments = ...;
// join the data
var query = from plan in plans
join assignment in assignments on plan.PlanCode equals assignment.PlanCode
select new { PlanCode = plan.PlanCode, Divisions = assignment.Divisions };
// reach the joined data
foreach (var planDivision in query) {
... = planDivision.PlanCode;
... = planDivision.Divisions;
...
}
}

(List<Dictionary<Object, Object>> in Linq to extract data

I have a data definition
I Deserialize JSON to this object
#return is JSON
JsonConvert.DeserializeObject<List<Dictionary<Object, Object>>>(utils.RemoveJsonOuterClass("GetTable", JsonConvert.DeserializeObject(#return).ToString()));
olist = [
[{
"item": 1
"Name "One"
}],
[{
"item": 2
"Name "Two"
}],
[{
"item": 1
"Name "One Two"
}]
];
This is a List<Dictionary<Object, Object>>
I need to find all of the items where "item" == 1.
Can I Use Linq? or is there any other way while using a large amount of data?
First: Your json is not correct fix that.
A colon should be present between Name and value.
A comma should be present after item value
and then change your code as below
//Create a class matching response object
public class ResponseItem
{
[JsonProperty("item")]
public int Item { get; set; }
public string Name { get; set; }
}
var responseJson = utils.RemoveJsonOuterClass("GetTable",
JsonConvert.DeserializeObject(#return).ToString();
var responseData = Newtonsoft.Json.JsonConvert.DeserializeObject<List<List<ResponseItem, ResponseItem>>>(responseJson);
Then use foreach with Where and apply condition
foreach (var responseObject in responseData.Where(x=>x.First().Item.Equals(1)))
{
}
Where is deferred execution and on each loop, it returns an object.
Here is the screenshot of my local execution.
Don't know if u're right with the object type. But the task is easy to solve:
static void Main(string[] args)
{
// Build the object
List<Dictionary<int, TestObject>> list = new List<Dictionary<int, TestObject>>();
// fill it with dictionaries
list.Add(new List<TestObject>()
{
new TestObject(){ Id = 1, Name = "One" },
new TestObject() { Id = 2, Name = "Two" },
new TestObject() { Id = 3, Name = "Three" }
}.ToDictionary(d => d.Id));
list.Add(new List<TestObject>()
{
new TestObject() { Id = 2, Name = "Two" },
new TestObject() { Id = 3, Name = "Three" }
}.ToDictionary(d => d.Id));
list.Add(new List<TestObject>()
{
new TestObject(){ Id = 1, Name = "One" },
new TestObject() { Id = 2, Name = "Two" }
}.ToDictionary(d => d.Id));
// Let's build a single list to work with
IEnumerable<TestObject> completeList = list.SelectMany(s => s.Values);
// aaaand filter it
IEnumerable<TestObject> filteredList = completeList.Where(l => l.Id == 1);
}
public class TestObject
{
public int Id { get; set; }
public string Name { get; set; }
}
Most part is initialization ;-)

Categories