Syntactic sugar, simplify list initialisation like dictionary - c#

To initialize a List<T> you have to use individual type initializer for each element - something like this:
List<Cat> cats = new List<Cat>
{
new Cat { Name = "Sylvester", Age = 8 },
new Cat { Name = "Whiskers", Age = 2 },
new Cat { Name = "Sasha", Age = 14 }
};
But for Dictionary you don't have to use the KeyValuePair initializer:
var numbers = new Dictionary<int, string>
{
{ 19, "nineteen" },
{ 23, "twenty-three" },
{ 42, "forty-two" }
};
Dictionary seems to be using the public void Add(TKey key, TValue value), and list also has a method public void Add(T item).
Is it possible to use a simplified initialisation for List<T> without individual initializers? Is it possible to List<T> to do it?

The only way you can do that is if you create your own list type with a Add(string name, int age) method.
public class Cat
{
public string Name { get; set; }
public int Age { get; set; }
}
public class CatList : List<Cat>
{
public void Add(string name, int age) => Add(new Cat { Name = name, Age = age });
}
Then you can run this code
var list = new CatList
{
{ "Sylvester", 8 },
{ "Whiskers", 2 },
{ "Sasha", 14 }
};
Console.WriteLine(string.Join(";", list.Select(c => $"{c.Name} is {c.Age}")));
But honestly I'm not sure it's worth it as you'd need to create custom collection types for each underlying type you want to store to be able to do this.
Another option is to initialize a list of value tuples and then translate it.
var list = new List<(string Name, int Age)>
{
( "Sylvester", 8 ),
( "Whiskers", 2 ),
( "Sasha", 14 )
}
.Select(x => new Cat { Name = x.Name, Age = x.Age })
.ToList();
Console.WriteLine(string.Join(";", list.Select(c => $"{c.Name} is {c.Age}")));

In C# 6 you can also use Extension Add methods in collection initializers so that you don't need to create own collection type:
public static class CatListExtensions
{
public static void Add(this List<Cat> list, string name, int age)
{
list.Add(new Cat {Name = name, Age = age});
}
}
After adding such extension method you will be able to use it in collection initializer:
List<Cat> list = new List<Cat>
{
{"Sylvester", 8},
{"Whiskers", 2},
{"Sasha", 14}
};

Collection initializers for List and Dicitionary use internally Add method, for List it is public void Add (T item); and for dictionary it is public void Add (TKey key, TValue value);, so Dictionary collection initializer does not actually construct KeyValuePair to pass it to the Add method and this code:
var numbers = new Dictionary<int, string>
{
{ 19, "nineteen" },
{ 23, "twenty-three" },
{ 42, "forty-two" }
};
Is translated into something like this:
Dictionary<int, string> dictionary = new Dictionary<int, string>();
dictionary.Add(19, "nineteen");
dictionary.Add(23, "twenty-three");
dictionary.Add(42, "forty-two");
You should be able to implement your own collection of Cat's which will have Add method with all needed parameters so you can initialize it without passing the type name.

Related

Convert List to List<Dictionary> efficiently without foreach in c# [duplicate]

This question already has answers here:
How can I convert a class into Dictionary<string,string>?
(8 answers)
Closed 4 years ago.
Assume I have a list of class
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
Now i need to convert into List of Dictionary as
Assume it as List<Dictionary<key, value>>PersonDict, below is the structure of each indexes of the same. The key name should be dynamically populated as Property name in the class Person.
key : "Id", Value : Person.Id
key : "Name", Value : Person.Name
key : "Age", Value : Person.Age
Can anyone please help, I have already tried "for each" and "Parallel For each" loop but its taking lot of time, I have 3 millions of record to convert and its taking hours.
Here is a working implementation:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
// Create some sample records
var persons = new List<Person>(){
new Person(){Id = 1, Name = "Bob", Age = 30},
new Person(){Id = 2, Name = "Jane", Age = 31},
new Person(){Id = 3, Name = "Mary", Age = 32}
};
// Use Reflection to retrieve public properties
var properties = typeof(Person).GetProperties();
// Create a list to store the dictionaries
var listOfDictionary = new List<Dictionary<string, string>>();
// For each person class
foreach(var person in persons){
// Create a new dictionary
var dict = new Dictionary<string,string>();
// For each property
foreach(var prop in properties){
// Add the name and value of the property to the dictionary
dict.Add(prop.Name, prop.GetValue(person).ToString());
}
// Add new dictionary to our list
listOfDictionary.Add(dict);
}
// Check the contents of our list
foreach(var dict in listOfDictionary){
Console.WriteLine(string.Join(",", dict.Keys));
Console.WriteLine(string.Join(",", dict.Values));
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}
You mention that you have millions of records to convert. It may be not the best idea to create millions of Dictionary<> instances, nor to keep them all in memory. However, it is difficult to recommend something without knowing what is your end goal.
This is pretty much the same as the other answers here, but using a slightly more terse syntax. Just a matter of preference.
List<Person> persons = new List<Person> {
new Person { Id = 1, Name = "Sally", Age = 10 },
new Person { Id = 2, Name = "Bob", Age = 9 },
};
List<Dictionary<string, string>> listOfDictionaries =
persons
.Select(p => new Dictionary<string, string>
{
["Id"] = p.Id.ToString(),
["Name"] = p.Name,
["Age"] = p.Age.ToString(),
})
.ToList();

Multi-Type and Multi-Dimensional Array (or List or Dictionary)?

Couldnt find exact question... I want to create a list(or dictionary or array, whatever it is called in C#, .NET), where i can store different types of arrays/lists/dicts.
for example, in PHP, i do in this way:
$x= array (
'Names'=> array( "James", "Nicolas", "Susan"), //Strings
'Age'=> array( 18, 52, 37), //Int
'Male'=> array( true, true, false), //Bool
);
How to achieve similar in C# / .NET ?
p.s. or if possible, Multi-Multi types, like:
$y = array (
$x => (
..... multi-element, like above
),
$z => (
..... multi-element, like above
)
);
In pre-version 7, C#, you have to create a class that can store your lists:
class Item {
public List<string> Names { get; }
public List<int> Ages { get; }
public List<bool> Males { get; }
}
You can also use a tuple, with the disadavantage of not having descriptive property names:
Tuple<List<string>, List<int>, List<bool>> tuple =
Tuple.Create(new List<string>(), new List<int>(), new List<bool>());
In C# 7 you can use value tuples without having to create a class:
(List<string> Names, List<int> Ages, List<bool> Males) itemLists =
(new List<string>(), new List<int>(), new List<bool>());
And access the components like this:
List<string> names = itemLists.Names;
You should though seriously consider to not create a class that contains lists, but a list that contains classes (or tuples). Here is an example with C# 7 value tuples:
List<(string Name, int Age, bool Male)> list = new List<(string, int, bool)>();
This construct is usually easier to handle, because you can loop one list and then handle one item that contains all related data.
Create a class that holds the information as List<string>, List<int> and List<string>. However a much better approach is to hold all the information for a single entity was a single class and store a list of those items:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public bool Male { get; set; }
}
Now store instances of that type in a list:
var list = new List<Person> {
new Person { Name = "James", Age = 18, Male = true },
new Person { Name = "Nicolas", Age = 52, Male = true },
new Person { Name = "Susan", Age = 37, Male = false }
};
This way you don´t need to "synchronize" all three lists, you have only one list instead.
If you really must use the approach you described define a class holding three different lists:
class Persons
{
public List<string> Names { get; set; }
public List<int> Ages { get; set; }
public List<bool> Male { get; set; }
}
Now you can create your persons as follows:
var persons = new Persons {
Names = new List<string> { "James", "Nicolas", "Susan"},
Ages = new List<int> { 17, 53, 37 },
Male = new List<bool> { true, true, false }
}
However this is quite difficult as every time you delete a name for example you´d also have to delete the appropriate age- and male-element also. Something like this:
persons.Names.RemoveAt(1);
persons.Ages.RemoveAt(1);
persons.Male.RemoveAt(1);
As #HimBromBeere said you may create a Person class:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public bool Male { get; set; }
}
Now you need to define another class that would store a result
public class Result
{
public List<string> Names { get; } = new List<string>();
public List<int> Age { get; } = new List<int>();
public List<bool> Male { get; } = new List<bool>();
}
At this time you can convert list of persons to your expected output with Linq
var persons = new List<Person> {
new Person { Name = "James", Age = 18, Male = true },
new Person { Name = "Nicolas", Age = 52, Male = true },
new Person { Name = "Susan", Age = 37, Male = false }
};
var result = persons.Aggregate(new Result(), (c, n) =>
{
c.Names.Add(n.Name);
c.Age.Add(n.Age);
c.Male.Add(n.Male);
return c;
});
If you are using C# 3 or higher you can use anonymous objects:
var x = new
{
Names = new[] {"James", "Nicolas", "Susan"},
Age = new[] {18, 52, 37},
Male = new[] {true, true, false}
};
for your second example you may use this code:
var y = new
{
x = new
{
Names = new[] {"James", "Nicolas", "Susan"},
Age = new[] {18, 52, 37},
Male = new[] {true, true, false}
},
// z = new { /* same code as above */ }
};

Create a subset of an object based off an array of property names

I have a class and an array of property names defined as follows:
public class Dog {
public string Name { get; set; }
public string Breed { get; set; }
public int Age { get; set; }
}
var desiredProperties = new [] {"Name", "Breed"};
I also have a method that returns a list of dog objects:
List<Dog> dogs = GetAllDogs();
Is there an way I can return a subset of dogs that only contain the properties defined within the desiredProperties array? Eventually, this resulting list will be serialized to JSON.
I have been struggling with this problem for some time now, considering that the user will be allowed to specify any combination of properties (assuming they are all valid) as the output within the array. Some more examples:
var desiredProperties = new [] {"Name", "Age"};
// Sample output, when serialized to JSON:
// [
// { Name: "Max", Age: 5 },
// { Name: "Spot", Age: 2 }
// ]
var desiredProperties = new [] {"Breed", "Age"};
// [
// { Breed: "Scottish Terrier", Age: 5 },
// { Breed: "Cairn Terrier", Age: 2 }
// ]
you can write a function to do that. Use the extension method below.
public static class Extensions
{
public static object GetPropertyValue(this object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName).GetValue(obj);
}
public static List<Dictionary<string, object>> FilterProperties<T>(this IEnumerable<T> input, IEnumerable<string> properties)
{
return input.Select(x =>
{
var d = new Dictionary<string, object>();
foreach (var p in properties)
{
d[p] = x.GetPropertyValue(p);
}
return d;
}).ToList();
}
}
Test it like
var dogs = GetAllDogs();
var f1 = dogs.FilterProperties(new[]
{
"Name", "Age"
});
var f2 = dogs.FilterProperties(new[]
{
"Breed", "Age"
});
Console.WriteLine(JsonConvert.SerializeObject(f1));
Console.WriteLine(JsonConvert.SerializeObject(f2));
and result is
[{"Name":"Spot","Age":2},{"Name":"Max","Age":5}]
[{"Breed":"Cairn Terrier","Age":2},{"Breed":"Scottish Terrier","Age":5}]
I don't have a clue if this is the most efficient way to do it, but it's a way of doing it:
var list = new List<Dog>();
list.Add(new Dog {Name = "Max", Breed = "Bull Terrier", Age = 5});
list.Add(new Dog {Name = "Woofie", Breed = "Collie", Age = 3});
var desiredProperties = new[] {"Name", "Breed"};
var exportDogs = new List<Dictionary<string, object>>();
foreach(var dog in list)
{
var exportDog = new Dictionary<string, object>();
foreach(var property in desiredProperties)
{
exportDog[property] = dog.GetType().GetProperty(property).GetValue(dog, null);
}
exportDogs.Add(exportDog);
}
var output = JsonConvert.SerializeObject(exportDogs);
The output will look like this:
[{"Name":"Max","Breed":"Bull Terrier"},{"Name":"Woofie","Breed":"Collie"}]
If, however, you don't need to dynamically access properties, it's a lot better to do something like this:
var output = list.Select(dog => new {dog.Name, dog.Breed});
Then just serialize the output.
something like this...not tested...
var desiredProperties = new [] {"Name", "Breed"};
var lst = (from asm in AppDomain.CurrentDomain.GetAssemblies()
from asmTyp in asm.GetTypes()
where typeof(dog).IsAssignableFrom(asmTyp) && desiredProperties.All(p=> PropertyExists(asmTyp, p))
select asmTyp).ToArray();
private bool PropertyExists(Type dogType, string name)
{
bool ret=true;
try{ dogType.GetProperty(name);}
catch{ret=false};
return ret;
}

Convert Lambda into Linq Statement with nested for loop

Is there a way to rewrite the GetTransformedCollection method below so that it uses a Linq statement and not an expression? I'm currently trying to get around the “A lambda expression with a statement body cannot be converted to an expression tree” error.
public class Obj1
{
public int Id { get; set; }
public string[] Names { get; set; }
public string[] Tags { get; set; }
}
public class EntCollections
{
private List<Obj1> _results;
[SetUp]
public void SetUp()
{
_results = new List<Obj1>
{
new Obj1 {Id = 1, Names = new[] {"n1"}, Tags = new[] {"abc", "def"}},
new Obj1 {Id = 2, Names = new[] {"n2", "n3"}, Tags = new[] {"ghi"}},
new Obj1 {Id = 3, Names = new[] {"n1", "n3"}, Tags = new[] {"def", "xyz"}}
};
}
private static Dictionary<string, List<string>>
GetTransformedCollection(IEnumerable<Obj1> results)
{
var list = new Dictionary<string, List<string>>();
foreach (var result in results)
{
foreach (var id in result.Names)
{
if (list.ContainsKey(id))
{
list[id].AddRange(result.Tags);
}
else
{
list.Add(id, result.Tags.ToList());
}
}
}
return list;
}
[Test]
public void Test()
{
var list = GetTransformedCollection(_results);
Assert.That(list["n1"], Is.EquivalentTo(new [] { "abc", "def", "def", "xyz" }));
Assert.That(list["n2"], Is.EquivalentTo(new [] { "ghi" }));
Assert.That(list["n3"], Is.EquivalentTo(new [] { "ghi", "def", "xyz" }));
}
P.s I'm not too worried about the result type being a Dictionary, that was just the simplist way to express it as a return type.
I would personally use an ILookup, which is a good bet whenever you have a Dictionary<T1, List<T2>>, and is built with ToLookup():
// Flatten the objects (lazily) to create a sequence of valid name/tag pairs
var pairs = from result in results
from name in result.Names
from tag in result.Tags
select new { name, tag };
// Build a lookup from name to all tags with that name
var lookup = pairs.ToLookup(pair => pair.name, pair => pair.tag);
Idea is to find all keys for resulting dictionary and then find corresponding values from original sequence of Obj1
var distinctNames = results.SelectMany(val => val.Names).Distinct();
return distinctNames
.ToDictionary(name => name,
name => results
.Where(res => res.Names.Contains(name))
.SelectMany(res => res.Tags)
.ToList());

Object Initializer for object collections

I am wanting to find out if there is a way to initialize a List<T> where T is an object much like a simple collection gets initialized?
Simple Collection Initializer:
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Object Collection Initilizer:
List<ChildObject> childObjects = new List<ChildObject>
{
new ChildObject(){ Name = "Sylvester", Age=8 },
new ChildObject(){ Name = "Whiskers", Age=2 },
new ChildObject(){ Name = "Sasha", Age=14 }
};
The question is, how and if you can do something like this?
List<ChildObject> childObjects = new List<ChildObject>
{
{ "Sylvester", 8} , {"Whiskers", 2}, {"Sasha", 14}
};
If you look at the docs for collection initializers, it's all about the collection's Add method. Just subclass the closed generic List over your type and make an Add method with the naked parameters. Like
public class MyColl : List<ChildObject>
{
public void Add(string s1, int a1, int a2)
{
base.Add(new ChildObject(s1, a1, a2));
}
}
public class ChildObject
{
public ChildObject(string s1, int a1, int a2)
{
//...
}
}
Then calling it looks like:
static void Main(string[] args)
{
MyColl x = new MyColl
{
{"boo", 2, 4},
{"mang", 3, 5},
};
}
The best you can probably do is something like this:
public class MyListOfChildObjects : List<ChildObject>
{
public void Add( string name, int age )
{
Add ( new ChildObject { Name = name, Age = age } );
}
}
var childObjects = new MyListOfChildObjects
{
{ "Sylvester", 8 } , { "Whiskers", 2 }, { "Sasha", 14 }
};
You could make it more generic, but how would you know which arguments should be bound to each property?
public class MyList<T> : List<T>
{
public void Add( params object[] arguments )
{
// what order should I bind the values to?
}
}
var childObjects = new MyList<ChildObject>
{
{ "Sylvester", 8 } , { "Whiskers", 2 }, { "Sasha", 14 }
};
You can get close by creating your own collection which extends List<ChildObject> and provide your own add method:
public class ChildObjectCollection : List<ChildObject>
{
public void Add(string name, int age)
{
this.Add(new ChildObject(name, age));
}
}
You can then initialise it like this:
var childObjects = new ChildObjectCollection
{
{ "Sylvester", 8} , {"Whiskers", 2}, {"Sasha", 1 }
};
You can't do this without creating your own class derived from List<ChildObject> as per Lee's answer. It's unfortunate that extension methods aren't considered for collection initalizers, otherwise this would work:
// This doesn't work, but it would if collection initializers checked
// extension methods.
using System;
using System.Collections.Generic;
public class ChildObject
{
public string Name { get; set; }
public int Age { get; set; }
}
public static class Extensions
{
public static void Add(this List<ChildObject> children,
string name, int age)
{
children.Add(new ChildObject { Name = name, Age = age });
}
}
class Test
{
static void Main(string[] args)
{
List<ChildObject> children = new List<ChildObject>
{
{ "Sylvester", 8 },
{ "Whiskers", 2 },
{ "Sasha", 14 }
};
}
}
The closest you can get to that is to create a parameterized constructor on your class that accepts those arguments. That won't get you all the way, but you can at least do this:
List<ChildObject> childObjects = new List<ChildObject>
{
new ChildObject("Sylvester", 8) ,
new ChildObject("Whiskers", 2),
new ChildObject("Sasha", 14)
};

Categories

Resources