Say I have this object:
dynamic foo = new ExpandoObject();
foo.bar = "fizz";
foo.bang = "buzz";
How would I remove foo.bang for example?
I don't want to simply set the property's value to null--for my purposes I need to remove it altogether. Also, I realize that I could create a whole new ExpandoObject by drawing kv pairs from the first, but that would be pretty inefficient.
Cast the expando to IDictionary<string, object> and call Remove:
var dict = (IDictionary<string, object>)foo;
dict.Remove("bang");
You can treat the ExpandoObject as an IDictionary<string, object> instead, and then remove it that way:
IDictionary<string, object> map = foo;
map.Remove("Jar");
MSDN Example:
dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
((IDictionary<String, Object>)employee).Remove("Name");
You can cast it as an IDictionary<string,object>, and then use the explicit Remove method.
IDictionary<string,object> temp = foo;
temp.Remove("bang");
Related
I am looking to dynamically create tuples of a given size that have all the same type.
So if I wanted a tuple of strings of size three I would get Tuple<string, string, string>
I have tried both to pass strings into the angle brackets for Tuple like this:
string foo = "string, string, string";
Tuple<foo> testTuple = Tuple<foo>(~parameters~);
and to pass an array of types into the angle brackets like this:
List<Type> types = new List<Type>() {"".GetType(), "".GetType(), "".GetType()};
Tuple<types> testTuple = Tuple<foo>(~parameters~);
Neither one of these worked. Does anyone know how to make the described dynamic tuples?
(The reason I want to do this is to use tuples inside of a dictionary like
Dictionary<Tuple<# strings>, int> testDictionary = new Dictionary<Tuple<x # strings>, int>();
Using tuples here is more useful than HashSets because the comparison in tuples is by components instead of by reference so that if I have
Tuple<string, string> testTuple1 = new Tuple<string, string>("yes", "no");
Tuple<string, string> testTuple2 = new Tuple<string, string>("yes", "no");
Dictionary<Tuple<string, string>, string> testDictionary = new Dictionary<Tuple<string, string>, string>() {
{testTuple1, "maybe"}
};
Console.WriteLine(testDict[testTuple2]);
it writes "maybe". If you run this same test with HashSets, it throws an error. If there is a better way to accomplish this same thing, that would also be useful.)
You could do something like this using reflection:
public static object GetTuple<T>(params T[] values)
{
Type genericType = Type.GetType("System.Tuple`" + values.Length);
Type[] typeArgs = values.Select(_ => typeof(T)).ToArray();
Type specificType = genericType.MakeGenericType(typeArgs);
object[] constructorArguments = values.Cast<object>().ToArray();
return Activator.CreateInstance(specificType, constructorArguments);
}
That will give you a tuple for a variable number of elements.
"Does anyone know how to make the described dynamic tuples?"
You can just use Tuple.Create(). For a 3-tuple:
var tupleOfStrings = Tuple.Create("string1", "string2", "string3");
var tupleOfInts = Tuple.Create(1, 2, 3);
Make custom type with List<string> inside (or List<T> if you want generic solution)
Override object.Equals: Use Enumerable.SequenceEqual for inner list in your custom class
Override object.GetHashCode() in order to use your type in dictionary
I have the following code, where page.Fields is an ExpandoObject. I'm iterating through some user-defined properties and adding them to an Expando, cast to an IDictionary<string,string> to allow me to add new field name / values dynamically, but when I set the Fields property to the value of props, serializing afterwards only gives names with blank values of {}. Why?
page.Fields.Foo = "asdf";
var test = JsonConvert.SerializeObject(page); // shows Foo=asdf in the json
// attach all fields to the page object, casting to an IDictionary to be able to add var names
var props = new ExpandoObject() as IDictionary<string, Object>;
foreach (string key in Request.Form.Keys)
{
if (key.StartsWith("Fields."))
{
var fieldName = key.Substring(key.IndexOf(".") + 1);
props.Add(fieldName, Request.Form[key]);
}
}
var test2 = JsonConvert.SerializeObject(props); // blank values of {}
page.Fields = props as ExpandoObject;
// loses the values for the Fields property
test = JsonConvert.SerializeObject(page);
UPDATE the curse of Nancy strikes, the Request.Form values turned out to be dynamic, so I had to .ToString() it to make it fit into the expected IDictionary<string,string>
To correctly serialize the data you must declare the variable as dynamic, not as an ExpandoObject, JSON .net uses reflection to retrieve properties, if it's a dynamic it casts it as an ExpandoObject and uses the keys as property names, but if you pass the ExpandoObject directly it tries to retrieve the properties from the ExpandoObject type.
Just change
var props = new ExpandoObject() as IDictionary<string, Object>;
to
var props = new ExpandoObject();
var iProps = props as IDictionary<string, Object>;
Use iProps to add the data and pass props to the serialization.
EDIT:
You're storing the value in "Page.Fields", this must be dynamic also.
I'm suspect it's a defect and you are not getting anything for Request.Form.Keys that's matches Field. criteria.
your code works fine for me if I've a page class with dynamic Fields property
This is for a MVVM based WPF project:
I am using an ExpandoObject in a view model for a dialog, which works very nicely since it implements INotifyPropertyChanged and I can bind to properties of the object directly in XAML.
However, to account for the user manipuating data but then hitting cancel I need to make a copy of the ExpandoObject to restore the original content.
In the dialog no properties are added to the object.
How can I clone it?
Shallow copy:
static ExpandoObject ShallowCopy(ExpandoObject original)
{
var clone = new ExpandoObject();
var _original = (IDictionary<string, object>)original;
var _clone = (IDictionary<string, object>)clone;
foreach (var kvp in _original)
_clone.Add(kvp);
return clone;
}
Deep copy:
static ExpandoObject DeepCopy(ExpandoObject original)
{
var clone = new ExpandoObject();
var _original = (IDictionary<string, object>)original;
var _clone = (IDictionary<string, object>)clone;
foreach (var kvp in _original)
_clone.Add(kvp.Key, kvp.Value is ExpandoObject ? DeepCopy((ExpandoObject)kvp.Value) : kvp.Value);
return clone;
}
As a remaining believer in static typing, eugh...
That being said, it looks like ExpandoObject implements IDictionary<string, object>:
dynamic foo1d = new ExpandoObject();
foo1d.a = "test";
dynamic foo2d = new ExpandoObject();
foreach (var kvp in (IDictionary<string, object>)foo1d)
{
((IDictionary<string, object>)foo2d).Add(kvp);
}
Debug.Assert(foo1d.a == foo2d.a);
or in VB:
Dim foo1d As Object = New ExpandoObject
Dim foo2d As Object = New ExpandoObject
foo1d.a = "foo"
Dim cloneDictionary = CType(foo2d, IDictionary(Of String, Object))
For Each line In CType(foo1d, IDictionary(Of String, Object))
cloneDictionary.Add(line.Key, line.Value)
Next
I will note that this is only a shallow clone. You would have to do further work do clone referenced objects.
var ExpandoObjs = GetDynamicList();
//clone
var clonedExpandos = ExpandoObjs.Cast<dynamic>().Select(x => x).ToList();
//modify and sort
var transformedExpandos = ExpandoObjs.Cast<dynamic>().Select(x =>
{
x.url= x.url + " some more stuff";
return x;
}).OrderBy(x => x.order).ToList();
as long as url is a property of the dynamic object, this will work.
It's useful if you're consuming a service call that doesn't have concrete types and you want to modify/sort the result.
I have a problem in creating dynamic objects. Please find the below code,
List<object> membersList = new List<object>();
foreach(var members in activityMembers){
dynamic myObject = new System.Dynamic.ExpandoObject();
myObject.MemberNumber = members.MemberNumber;
myObject.MemberName = members.Name;
foreach (var activity in members.ActivityList)
{
myObject.[activity.ActivityName] = activity.Minutes;
}
membersList.Add(myObject);
}
there inside the second foreach loop, i need to generate the properties to all activities. for example if there are 4 activities in members.ActivityList, then 4 properties needs to be generated for object.
myObject.Activity1 = 10;
myObject.Activity2 = 20;
myObject.Activity3 = 30;
myObject.Activity4 = 40;
How can i do this? What i did wrong here?
Regards,
Karthik.
Remove the . when you are indexing the object i.e. change
myObject.[activity.ActivityName] = activityMinutes;
to
myObject[activity.ActivityName] = activity.Minutes;
Actually this won't solve your problem straight away, it will compile fine but when you attempt to run it will throw a RuntimeBinderException as you can't index into a ExpandoObject directly. You need to cast it as a dictionary before iterating (that's effectively what it is) e.g.
var dict = (IDictionary<string, object>)myObject;
...
dict[activity.ActivityName] = activity.Minutes;
I suspect you need to treat the ExpandoObject as a dictionary for that part:
IDictionary<string, object> dictionary = myObject;
foreach (var activity in members.ActivityList)
{
dictionary[activity.ActivityName] = activity.Minutes;
}
That's the way of assigning properties to an ExpandoObject when you don't know the property name at compile-time.
Normally, we can create properties like this,
dynamic expando = new ExpandoObject();
expando.Price = 45k;
expando.Value = "Good";
In my case, I won't know the properties such as "Price" or "Value" until runtime. How, can I create such dynamic properties. Something like,
dynamic expando = new ExpandoObject();
expando[properties[0]] = 45k;
expando[properties[1]] = "Good";
expando[properties[2]] = "Red";
expando[properties[3]] = 8;
Anyway to achieve this kind of behavior.
Just use the fact that it implements IDictionary<string, Object>:
IDictionary<string, Object> expando = new ExpandoObject();
expando[properties[0]] = 45;
expando[properties[1]] = "Good";
expando[properties[2]] = "Red";
expando[properties[3]] = 8;
dynamic d = expando;
// Now use the properties as normal
On the other hand, if you don't know the properties until execution time, what's actually going to consume them? It may still make sense to use ExpandoObject - but equally it may make sense to use Dictionary<string, object> to start with.