I've just read this question.
If we have property of dictionary type:
public class Test
{
public Dictionary<string, string> Dictionary { get; set; } = new Dictionary<string, string>
{
{"1", "1" },
{"2", "2" },
};
}
Then we can construct object and add value to it
var test = new Test { Dictionary = { { "3", "3" } } };
Console.WriteLine(test.Dictionary.Count); // 3
And I don't understand the point why such a confusing syntax to add items exists? When looking at someone else code it's very easy to confuse it with very similarly looking
var test = new Test { Dictionary = new Dictionary<string, string> { { "3", "3" } } };
Console.WriteLine(test.Dictionary.Count); // 1
I'd be more OK with it if following would be possible (but it's not):
var dictionary = new Dictionary<string, string> { { "1", "1" } };
...
// adding a new value
dictionary = { { "2" , "2"} }; // invalid expression term '{'
So why this form of adding was needed and exists? For interviews?
The collection initializer syntax is simply a convenient way of initializing collections (including dictionaries) as part of a complex object model using an object initializer. For example:
var model = new SomeModel {
Name = "abc",
Id = 42,
SpecialMaps = {
{ "foo", "bar" },
{ "magic", "science" },
}
};
If you don't like it: just don't use it; but the equivalent with manual .Add is IMO much less elegant - a lot of things are taken care of automatically, such as only reading the property once. The longer version that actually creates the collection at the same time works very similarly.
Note that there is also an indexer variant now:
var model = new SomeModel {
Name = "abc",
Id = 42,
SpecialMaps = {
["foo"] = "bar",
["magic"] ="science",
}
};
This is very similar, but instead of using collection.Add(args); it uses collection[key] = value;. Again, if it confuses you or offends you: don't use it.
Take this example where the constructor of Thing creates a Stuff and the constructor of Stuff creates the Foo list
var thing = new Thing();
thing.Stuff.Foo.Add(1);
thing.Stuff.Foo.Add(2);
thing.Stuff.Foo.Add(3);
And now you can simplify it to the following with initializers.
var thing = new Thing
{
Stuff.Foo = { 1, 2, 3 }
};
You can only use this type of initialization for a collection without first newing up the collection when nested because the collection can exist in this case, but cannot when assigning directly to a variable.
Ultimately this type of syntactic sugar is likely added by the language designers when they see code patterns that they think can be simplified.
Related
I would like to merge these two anonymous objects:
var man1 = new {
name = new {
first = "viet"
},
age = 20
};
var man2 = new {
name = new {
last = "vo"
},
address = "123 street"
};
Into a single one:
var man = new {
name = new {
first = "viet",
last = "vo"
},
age = 20,
address = "123 street"
};
I looked for a solution but found nothing clever.
Convert the anonymous object to ExpandoObject which is essentially a dictionary of string key and object value:
var man1Expando = man1.ToDynamic();
var man2Expando = man2.ToDynamic();
public static ExpandoObject ToDynamic(this object obj)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (var propertyInfo in obj.GetType().GetProperties())
{
var currentValue = propertyInfo.GetValue(obj);
if (propertyInfo.PropertyType.IsAnonymous())
{
expando.Add(propertyInfo.Name, currentValue.ToDynamic());
}
else
{
expando.Add(propertyInfo.Name, currentValue);
}
}
return expando as ExpandoObject;
}
I'm using a helper extension to establish whether a type is an anonymous one:
public static bool IsAnonymous(this Type type)
{
return type.DeclaringType is null
&& type.IsGenericType
&& type.IsSealed
&& type.IsClass
&& type.Name.Contains("Anonymous");
}
Then, merge two resulting expando objects into one, but recursively, checking for nested expando objects:
var result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
public static IDictionary<string, object> MergeDictionaries(
IDictionary<string, object> targetDictionary,
IDictionary<string, object> sourceDictionary,
bool overwriteTarget)
{
foreach (var pair in sourceDictionary)
{
if (!targetDictionary.ContainsKey(pair.Key))
{
targetDictionary.Add(pair.Key, sourceDictionary[pair.Key]);
}
else
{
if (targetDictionary[pair.Key] is IDictionary<string, object> innerTargetDictionary)
{
if (pair.Value is IDictionary<string, object> innerSourceDictionary)
{
targetDictionary[pair.Key] = MergeDictionaries(
innerTargetDictionary,
innerSourceDictionary,
overwriteTarget);
}
else
{
// What to do when target propety is nested, but source is not?
// Who takes precedence? Target nested property or source value?
if (overwriteTarget)
{
// Replace target dictionary with source value.
targetDictionary[pair.Key] = pair.Value;
}
}
}
else
{
if (pair.Value is IDictionary<string, object> innerSourceDictionary)
{
// What to do when target propety is not nested, but source is?
// Who takes precedence? Target value or source nested value?
if (overwriteTarget)
{
// Replace target value with source dictionary.
targetDictionary[pair.Key] = innerSourceDictionary;
}
}
else
{
// Both target and source are not nested.
// Who takes precedence? Target value or source value?
if (overwriteTarget)
{
// Replace target value with source value.
targetDictionary[pair.Key] = pair.Value;
}
}
}
}
}
return targetDictionary;
}
The overwriteTarget parameter decides which object takes priority when merging.
Usage code:
var man1 = new
{
name = new
{
first = "viet",
},
age = 20,
};
var man2 = new
{
name = new
{
last = "vo",
},
address = "123 street",
};
var man1Expando = man1.ToDynamic();
var man2Expando = man2.ToDynamic();
dynamic result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
and the result:
{
"name": {
"first": "viet",
"last": "vo"
},
"age": 20,
"address": "123 street"
}
Notice how I assigned the result to dynamic. Leaving compiler assign the type will leave you with expando object presented as IDictionary<string, object>. With a dictionary representation, you cannot access properties in the same manner as if it was an anonymous object:
var result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
result.name; // ERROR
That's why the dynamic. With dynamic you are losing compile time checking, but have two anonymous objects merged into one. You have to judge for yourself if it suits you.
There's nothing built-in in the C# language to support your use case. Thus, the question in your title needs to be answered with "Sorry, there is no easy way".
I can offer the following alternatives:
Do it manually:
var man = new {
name = new {
first = man1.name.first,
last = man2.name.first
},
age = man1.age,
address = man2.address
};
Use a class instead of an anonymous type for the resulting type (let's call it CompleteMan). Then, you can
create a new instance var man = new CompleteMan(); ,
use reflection to collect the properties and values from your "partial men" (man1 and man2),
assign those values to the properties of your man.
It's "easy" in the sense that the implementation will be fairly straight-forward, but it will still be a lot of code, and you need to take your nested types (name) into account.
If you desperately want to avoid non-anonymous types, you could probably use an empty anonymous target object, but creating this object (var man = new { name = new { first = (string)null, last = (string)null, ...) is not really less work than creating a class in the first place.
Use a dedicated dynamic data structure instead of anonymous C# classes:
The Newtonsoft JSON library supports merging of JSON objects.
Dictionaries can also be merged easily.
ExpandoObjects can be merged easily as well.
I'm starting using C# and have a lot to learn.
I'm trying to create a JSON file with a dictionary (thats easy).
JsonSerializer.Serialize(MyDictionary)
But it returns the data without descriptions...
{
"-0255504",
"1"
},
{
:"08000301",
:"1"
}
I want to to include some descriptions like:
{
"ArticleId":"-0255504",
"OrderedQuantity":"1"
},
{
"ArticleId":"08000301",
"OrderedQuantity":"1"
},
{
"ArticleId":"03820235",
"OrderedQuantity":"1"
}
For sure is easy to include and don't want to use List for my program.
Is available any method or property to modify the format?
I'm Using System.Text.Json;
You can project your dictionary items into anonymous (or not anonymous) types to get your property names:
Dictionary<string, string> d = new Dictionary<string, string>
{
{ "-0255504", "1" },
{ "-08000301", "1" },
};
string json = JsonSerializer.Serialize(d.Select(entry => new { ArticleId = entry.Key, OrderedQuantity = entry.Value }));
Is is possible to combine a List initializer and object initializer at the same time?
Given the following class definition:
class MyList : List<int>
{
public string Text { get; set; }
}
// we can do this
var obj1 = new MyList() { Text="Hello" };
// we can also do that
var obj2 = new MyList() { 1, 2, 3 };
// but this one doesn't compile
//var obj3 = new MyList() { Text="Hello", 1, 2, 3 };
Is this by design or is it just a bug or missing feature of the c# compiler?
No, looking at the definitions from section 7.6.10 of the C# spec, an object-or-collection-initializer expression is either an object-initializer or a collection-initializer.
An object-initializer is composed of multiple member-initializers, each of which is of the form initializer = initializer-value whereas a collection-initializer is composed of multiple element-initializers, each of which is a non-assigment-expression.
So it looks like it's by design - possibly for the sake of simplicity. I can't say I've ever wanted to do this, to be honest. (I usually wouldn't derive from List<int> to start with - I'd compose it instead.) I would really hate to see:
var obj3 = new MyList { 1, 2, Text = "Hello", 3, 4 };
EDIT: If you really, really want to enable this, you could put this in the class:
class MyList : List<int>
{
public string Text { get; set; }
public MyList Values { get { return this; } }
}
at which point you could write:
var obj3 = new MyList { Text = "Hello", Values = { 1, 2, 3, 4 } };
No, it's a not a bug. It is by design of the language.
When you write
var obj1 = new MyList() { Text="Hello" };
this is effectively translated by the compiler to
MyList temp = new MyList();
temp.Text = "Hello";
MyList obj = temp;
When you write
var obj2 = new MyList() { 1, 2, 3 };
this is effectively translated by the compiler to
MyList temp = new MyList();
temp.Add(1);
temp.Add(2);
temp.Add(3);
MyList obj2 = temp;
Note that in the first case you are using an object initializer, but in the second case you are using a collection initializer. There is no such thing as an object-and-collection intializer. You are either initializing the properties of your object, or you are initializing the collection. You can not do both, this is by design.
Also, you shouldn't derive from List<T>. See: Inheriting List<T> to implement collections a bad idea?
If you want to get something like this functionality, consider making a constructor argument:
var x = new CustomList("Hello World") { 1, 2, 3 }
I've just noticed that when you declare a List in c# you can put parentheses or curly braces at the end.
List<string> myList = new List<string>();
List<string> myList2 = new List<string>{};
Both these list appear to have the same functionality. Is there any actual difference caused by declaring them with parentheses or curly braces?
The use of curly braces { } is called a collection initializer. For types that implement IEnumerable the Add method would be invoked normally, on your behalf:
List<string> myList2 = new List<string>() { "one", "two", "three" };
Empty collection initializers are allowed:
List<string> myList2 = new List<string>() { };
And, when implementing an initializer, you may omit the parenthesis () for the default constructor:
List<string> myList2 = new List<string> { };
You can do something similar for class properties, but then it's called an object initializer.
var person = new Person
{
Name = "Alice",
Age = 25
};
And its possible to combine these:
var people = new List<Person>
{
new Person
{
Name = "Alice",
Age = 25
},
new Person
{
Name = "Bob"
}
};
This language feature introduced in C# 3.0 also supports initializing anonymous types, which is especially useful in LINQ query expressions:
var person = new { Name = "Alice" };
They also work with arrays, but you can further omit the type which is inferred from the first element:
var myArray = new [] { "one", "two", "three" };
And initializing multi-dimensional arrays goes something like this:
var myArray = new string [,] { { "a1", "b1" }, { "a2", "b2" }, ... };
Update
Since C# 6.0, you can also use an index initializer. Here's an example of that:
var myDictionary = new Dictionary<string, int>
{
["one"] = 1,
["two"] = 2,
["three"] = 3
};
They have different semantics.
List<string> myList = new List<string>();
The above line initializes a new List of Strings, and the () is part of the syntax of building a new object by calling its constructor with no parameters.
List<string> myList2 = new List<string>{};
The above line initializes a new List of Strings with the elements presented inside the {}. So, if you did List<string> myList2 = new List<string>{"elem1", "elem2"}; you are defining a new list with 2 elements. As you defined no elements inside the {}, it will create an empty list.
But why does the second line have no () ?
That makes part of a discussion in which omitting the parenthesis in this case represents a call to the default constructor. Take a look at This Link
The first version initialises an empty list. The second version is used to initialise the list with values. You wouldn't, or shouldn't, see the second version used without at least one instance of T.
So you could do something like:
List<string> Foo = new List<string>{"foo", "bar"};
or
List<T> Foo = new List<T>{SomeInstancesOfT};
This is useful in many places when initialising objects.
See https://msdn.microsoft.com/en-us/library/bb384062.aspx
To be short - there is no difference in the objects created.
But there's more:
What you are asking isn't a List specific question - it's a question of object and Collection initialization.
See here.
Can I retrieve basic information about all collections in a MongoDB with F#?
I have a MongoDB with > 450 collections. I can access the db with
open MongoDB.Bson
open MongoDB.Driver
open MongoDB.Driver.Core
open MongoDB.FSharp
open System.Collections.Generic
let connectionString = "mystring"
let client = new MongoClient(connectionString)
let db = client.GetDatabase(name = "Production")
I had considered trying to just get all collections then loop through each collection name and get basic information about each collection with
let collections = db.ListCollections()
and
db.GetCollection([name of a collection])
but the db.GetCollection([name]) requires me to define a type to pull the information about each collection. This is challenging for me as I don't want to have to define a type for each collection, of which there are > 450, and frankly, I don't really know much about this DB. (Actually, no one in my org does; that's why I'm trying to put together a very basic data dictionary.)
Is defining the type for each collection really necessary? Can I use the MongoCollection methods available here without having to define a type for each collection?
EDIT: Ultimately, I'd like to be able to output collection name, the n documents in each collection, a list of the field names in each collection, and a list of each field type.
I chose to write my examples in C# as i'm more familiar with the C# driver and it is a listed tag on the question. You can run an aggregation against each collection to find all top level fields and their (mongodb) types for each document.
The aggregation is done in 3 steps. Lets assume the input is 10 documents which all have this form:
{
"_id": ObjectId("myId"),
"num": 1,
"str": "Hello, world!"
}
$project Convert each document into an array of documents with values fieldName and fieldType. Outputs 10 documents with a single array field. The array field will have 3 elements.
$unwind the arrays of field infos. Outputs 30 documents each with a single field corresponding to an element from the output of step 1.
$group the fields by fieldName and fieldType to get distinct values. Outputs 3 documents. Since all fields with the same name always have the same type in this example, there is only one final output document for each field. If two different documents defined the same field, one as string and one as int there would be separate entries in this result set for both.
// Define our aggregation steps.
// Step 1, $project:
var project = new BsonDocument
{ {
"$project", new BsonDocument
{
{
"_id", 0
},
{
"fields", new BsonDocument
{ {
"$map", new BsonDocument
{
{ "input", new BsonDocument { { "$objectToArray", "$$ROOT" } } },
{ "in", new BsonDocument {
{ "fieldName", "$$this.k" },
{ "fieldType", new BsonDocument { { "$type", "$$this.v" } } }
} }
}
} }
}
}
} };
// Step 2, $unwind
var unwind = new BsonDocument
{ {
"$unwind", "$fields"
} };
// Step 3, $group
var group = new BsonDocument
{
{
"$group", new BsonDocument
{
{
"_id", new BsonDocument
{
{ "fieldName", "$fields.fieldName" },
{ "fieldType", "$fields.fieldType" }
}
}
}
}
};
// Connect to our database
var client = new MongoClient("myConnectionString");
var db = client.GetDatabase("myDatabase");
var collections = db.ListCollections().ToEnumerable();
/*
We will store the results in a dictionary of collections.
Since the same field can have multiple types associated with it the inner value corresponding to each field is `List<string>`.
The outer dictionary keys are collection names. The inner dictionary keys are field names.
The inner dictionary values are the types for the provided inner dictionary's key (field name).
List<string> fieldTypes = allCollectionFieldTypes[collectionName][fieldName]
*/
Dictionary<string, Dictionary<string, List<string>>> allCollectionFieldTypes = new Dictionary<string, Dictionary<string, List<string>>>();
foreach (var collInfo in collections)
{
var collName = collInfo["name"].AsString;
var coll = db.GetCollection<BsonDocument>(collName);
Console.WriteLine("Finding field information for " + collName);
var pipeline = PipelineDefinition<BsonDocument, BsonDocument>.Create(project, unwind, group);
var cursor = coll.Aggregate(pipeline);
var lst = cursor.ToList();
allCollectionFieldTypes.Add(collName, new Dictionary<string, List<string>>());
foreach (var item in lst)
{
var innerDict = allCollectionFieldTypes[collName];
var fieldName = item["_id"]["fieldName"].AsString;
var fieldType = item["_id"]["fieldType"].AsString;
if (!innerDict.ContainsKey(fieldName))
{
innerDict.Add(fieldName, new List<string>());
}
innerDict[fieldName].Add(fieldType);
}
}
Now you can iterate over your result set:
foreach(var collKvp in allCollectionFieldTypes)
{
foreach(var fieldKvp in collKvp.Value)
{
foreach(var fieldType in fieldKvp.Value)
{
Console.WriteLine($"Collection {collKvp.Key} has field name {fieldKvp.Key} with type {fieldType}");
}
}
}