how to flatten nested tuples in c#? - c#

I have nested tuples like - Tuple<Tuple<Tuple<string,string>, string>, string>.
I want to flatten them like Tuple<string,string,string,string>. I see that is can be done in f#. Is there a version of - F# flatten nested tuples - in c#

You can convert your Tuple to the flat list with recursion and Deep-First Search. Try this method:
public static IEnumerable<object> DFS(object t)
{
var type = t.GetType();
if (type.FullName?.StartsWith("System.Tuple") != true) // or check inheritanse from ITuple
yield return t;
var items = type.GetProperties()
.Where(p => p.Name.StartsWith("Item"))
.Select(p => p.GetValue(t))
.ToArray();
foreach (var item in items)
{
foreach (var innerItem in DFS(item))
{
yield return innerItem;
}
}
}
You can use it like this:
var input = Tuple.Create(Tuple.Create(Tuple.Create("a0", "a1"), "a2"), "b", "c");
var items = DFS(input).ToArray();
// items[2] would be "a2"
Please note that reflection may slow down your app so try to avoid it while it is possible

Assuming you insist in using this rather obnoxious design, here is how you could do it:
/// <summary>
/// Constructs a tuple our of an array of arguments
/// </summary>
/// <typeparam name="T">The type of argument.</typeparam>
/// <param name="values">The values.</param>
/// <returns></returns>
public static object ConstructTuple<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);
}
/// <summary>
/// Flattens a tupple into an enumeration using reflection.
/// </summary>
/// <typeparam name="T">The type of objects the nested tuple contains.</typeparam>
/// <param name="tuple">The tuple to flatten.</param>
/// <returns></returns>
public static IEnumerable<T> FlattenTupple<T>(object tuple)
{
List<T> items = new List<T>();
var type = tuple.GetType();
if (type.GetInterface("ITuple") == null)
throw new ArgumentException("This is not a tuple!");
foreach (var property in type.GetProperties())
{
var value = property.GetValue(tuple);
if (property.PropertyType.GetInterface("ITuple") != null)
{
var subItems = FlattenTupple<T>(value);
items.AddRange(subItems);
}
else
{
items.Add((T)value);
}
}
return items;
}
Sample usage:
Tuple<Tuple<Tuple<string, string>, string>, string> tuple =
new Tuple<Tuple<Tuple<string, string>, string>, string>(new Tuple<Tuple<string, string>, string>(new Tuple<string, string>("value1", "value2"), "value2b"), "value2c");
var items = FlattenTupple<string>(tuple);
var flattened = ConstructTuple(items.ToArray());
Console.WriteLine(flattened);
Output:
(value1, value2, value2b, value2c)
Sample 2 (integers):
Tuple<Tuple<Tuple<int, int>, int>, int> intTuple =
new Tuple<Tuple<Tuple<int, int>, int>, int>(new Tuple<Tuple<int, int>, int>(new Tuple<int, int>(1, 2), 3), 4);
var intItems = FlattenTupple<int>(intTuple);
var flattened2 = ConstructTuple(intItems.ToArray());
Console.WriteLine(flattened2);
Output:
(1, 2, 3, 4)

If you have a single item then create a converter which is easier to manage especially if you have a List eventually.
public Tuple<string, string, string, string> ConvertSomething(Tuple<Tuple<Tuple<string,string>, string>, string> original)
{
return new Tuple<string, string, string, string>
(
original.Item1.Item1.Item1,
original.Item1.Item1.Item2,
original.Item1.Item2,
original.Item2
);
}
Please note that Tuple is not meant to be modify along the way. If you do need it, it mean you actually have a very bad design. Best solution remain to rethink how things work.

Related

Get property names of a object as a Dictionary<string,string> in .net

I working on a string tokenizer function.
This is the sample object
var obj= new
{
Order= new
{
CustomerName = "John",
OrderTotal= "10.50",
Qty =2,
Address= new
{
Street= "Park Road",
Country= "UAS"
}
}
};
I need to get property vs values onto Dictionary<string, string>
expecting result :
<Order.CustomerName,"John">
<Order.OrderTotal,"10.50">
<Order.Qty ,"2">
<Order.CustomerName.Address.Street,"Park Road">
<Order.CustomerName.Address.Country,"USA">
This is how I tried
private Dictionary<string, string> GetValueByPropertyToken(object obj)
{
var result = new Dictionary<string, string>();
// get the type:
var objType = obj.GetType();
// iterate the properties
var prop = (from property in objType.GetProperties() select property).ToList();
foreach (var item in prop)
{
var name = item.Name;
var nextProperty = item?.GetValue(obj);
}
return result;
}
You can achieve this via recursion and reflection:
Extension.cs class:
public static class Extensions
{
/// <summary>
/// Extension method for object to explode to dictionary.
/// </summary>
/// <param name="values"></param>
/// <returns>Dictionary with key-value pairs for prefix and primite values</returns>
public static IDictionary<string, object> ToDictionary(this object values)
{
var dict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
GetValues("", values, dict);
return dict;
}
/// <summary>
/// Recursively go through obj and explode into key-value pairs for prefix + value
/// </summary>
/// <param name="prefix">Prefix for key</param>
/// <param name="obj">The object to explode</param>
/// <param name="dict">The resulting dictionary</param>
private static void GetValues(string prefix, object obj, Dictionary<string, object> dict)
{
var properties = TypeDescriptor.GetProperties(obj);
// Base case
if (properties.Count == 0) return;
// Go through all children objects
foreach (PropertyDescriptor property in properties)
{
// Get next object and prefix
var nextObject = property.GetValue(obj);
string nextPrefix = (String.IsNullOrEmpty(prefix) ? property.Name : $"{prefix}.{property.Name}");
// If it´s generic we continue down the object
// If it´s primitive we add it to the dictionary
if (nextObject.GetType().IsGenericType)
GetValues(nextPrefix, nextObject, dict);
else
dict.Add(nextPrefix, nextObject);
}
}
}
Program.cs class:
private static void Main(string[] args)
{
var obj = new
{
Order = new
{
CustomerName = "John",
OrderTotal = "10.50",
Qty = 2,
Address = new
{
Street = "Park Road",
Country = "UAS"
}
}
};
var dict = obj.ToDictionary();
foreach(string key in dict.Keys)
{
Console.WriteLine($"{key}: {dict[key]}");
}
}
The output:
Order.CustomerName: John
Order.OrderTotal: 10.50
Order.Qty: 2
Order.Address.Street: Park Road
Order.Address.Country: UAS
Be careful that this code probably won´t handle more complicated scenarios involving arrays and other complicated data structures. You will have to expand on this code to fulfill your needs.
firslty, you did not add dictionary object (result), secondly for sub element you have to call it again.
public Dictionary<string,string> GetDictionaryByObject(object obj)
{
{
var dict = new Dictionary<string, string>();
foreach (var prop in obj.GetType().GetProperties())
{
if (prop.PropertyType.IsPrimitive || prop.PropertyType == typeof(string))
{
dict.Add(prop.Name, prop.GetValue(obj).ToString());
}
else
{
var subDict = GetDictionaryByObject(prop.GetValue(obj));
foreach (var subProp in subDict)
{
dict.Add($"{prop.Name}.{subProp.Key}", subProp.Value);
}
}
}
return dict;
}
}
Result:

C# Equivalent of PHP http_build_query

I need to pass some data to a PHP page on a server from my C# client using HttpWebRequest. The expected data according to the documentation is an array of arrays, something like this:
$postData = array(
'label1' => 'myLabel',
'label2' => array(
'label2_1' => 3
'label2_2' => array(
'label2_2_1' => 3
)
)
);
The structure above is just an example. It can be very complicated and the structure itself is not constant.
In PHP there is a function named http_build_query which serializes these PHP nested arrays to a simple string, which can be sent as the data of a HTTP POST request. The problem is I need to call this PHP page from my C# application. I would like to represent these nested arrays either as nested Dictionary<string, object>s, or anonymous types.
How can I do that? What rules does http_build_query follow to produce its output string?
There is a very similar question Converting PHP array of arrays to C#, which does not solve my problem, unfortunately. The accepted answer recommends a solution for a fixed structure, the second one does not work at all.
Well, there doesn't seem to be anything built-in to .NET to allow you to do this. However, if you want to re-implement the PHP behavior in .NET, you can either white-box implement it by taking a look at the PHP source code, or black-box implement it by reading the PHP documentation of http_build_query and testing the function out on various inputs.
I took a black-box approach and created the following class:
/// <summary>
/// Helps up build a query string by converting an object into a set of named-values and making a
/// query string out of it.
/// </summary>
public class QueryStringBuilder
{
private readonly List<KeyValuePair<string, object>> _keyValuePairs
= new List<KeyValuePair<string, object>>();
/// <summary> Builds the query string from the given instance. </summary>
public static string BuildQueryString(object queryData, string argSeperator = "&")
{
var encoder = new QueryStringBuilder();
encoder.AddEntry(null, queryData, allowObjects: true);
return encoder.GetUriString(argSeperator);
}
/// <summary>
/// Convert the key-value pairs that we've collected into an actual query string.
/// </summary>
private string GetUriString(string argSeperator)
{
return String.Join(argSeperator,
_keyValuePairs.Select(kvp =>
{
var key = Uri.EscapeDataString(kvp.Key);
var value = Uri.EscapeDataString(kvp.Value.ToString());
return $"{key}={value}";
}));
}
/// <summary> Adds a single entry to the collection. </summary>
/// <param name="prefix"> The prefix to use when generating the key of the entry. Can be null. </param>
/// <param name="instance"> The instance to add.
///
/// - If the instance is a dictionary, the entries determine the key and values.
/// - If the instance is a collection, the keys will be the index of the entries, and the value
/// will be each item in the collection.
/// - If allowObjects is true, then the object's properties' names will be the keys, and the
/// values of the properties will be the values.
/// - Otherwise the instance is added with the given prefix to the collection of items. </param>
/// <param name="allowObjects"> true to add the properties of the given instance (if the object is
/// not a collection or dictionary), false to add the object as a key-value pair. </param>
private void AddEntry(string prefix, object instance, bool allowObjects)
{
var dictionary = instance as IDictionary;
var collection = instance as ICollection;
if (dictionary != null)
{
Add(prefix, GetDictionaryAdapter(dictionary));
}
else if (collection != null)
{
Add(prefix, GetArrayAdapter(collection));
}
else if (allowObjects)
{
Add(prefix, GetObjectAdapter(instance));
}
else
{
_keyValuePairs.Add(new KeyValuePair<string, object>(prefix, instance));
}
}
/// <summary> Adds the given collection of entries. </summary>
private void Add(string prefix, IEnumerable<Entry> datas)
{
foreach (var item in datas)
{
var newPrefix = String.IsNullOrEmpty(prefix)
? item.Key
: $"{prefix}[{item.Key}]";
AddEntry(newPrefix, item.Value, allowObjects: false);
}
}
private struct Entry
{
public string Key;
public object Value;
}
/// <summary>
/// Returns a collection of entries that represent the properties on the object.
/// </summary>
private IEnumerable<Entry> GetObjectAdapter(object data)
{
var properties = data.GetType().GetProperties();
foreach (var property in properties)
{
yield return new Entry()
{
Key = property.Name,
Value = property.GetValue(data)
};
}
}
/// <summary>
/// Returns a collection of entries that represent items in the collection.
/// </summary>
private IEnumerable<Entry> GetArrayAdapter(ICollection collection)
{
int i = 0;
foreach (var item in collection)
{
yield return new Entry()
{
Key = i.ToString(),
Value = item,
};
i++;
}
}
/// <summary>
/// Returns a collection of entries that represent items in the dictionary.
/// </summary>
private IEnumerable<Entry> GetDictionaryAdapter(IDictionary collection)
{
foreach (DictionaryEntry item in collection)
{
yield return new Entry()
{
Key = item.Key.ToString(),
Value = item.Value,
};
}
}
}
The code is pretty self-explanatory, but it accepts a dictionary, an array, or an object. If it's an top-level object, it serialized the properties. If it's an array, each element is serialized with the appropriate array index. If it's a dictionary, the key/values are serialized. Arrays and Dictionary-values that contain other arrays or dictionaries are flattened, similar to PHPs behavior.
For example, the following:
QueryStringBuilder.BuildQueryString(new
{
Age = 19,
Name = "John&Doe",
Values = new object[]
{
1,
2,
new Dictionary<string, string>()
{
{ "key1", "value1" },
{ "key2", "value2" },
}
},
});
// 0=1&1=2&2%5B0%5D=one&2%5B1%5D=two&2%5B2%5D=three&3%5Bkey1%5D=value1&3%5Bkey2%5D=value2
QueryStringBuilder.BuildQueryString(new object[]
{
1,
2,
new object[] { "one", "two", "three" },
new Dictionary<string, string>()
{
{ "key1", "value1" },
{ "key2", "value2" },
}
}
);
Generates:
Age=19&Name=John%26Doe&Values%5B0%5D=1&Values%5B1%5D=2&Values%5B2%5D%5Bkey1%5D=value1&Values%5B2%5D%5Bkey2%5D=value2
which is:
Age=19&Name=John%26Doe&Values[0]=1&Values[1]=2&Values[2][key1]=value1&Values[2][key2]=value2
Age=19
Name=John&Doe
Values[0]=1
Values[1]=2
Values[2][key1]=value1
Values[2][key2]=value2
Using NameValueCollection you could do this:
private string ToQueryString(NameValueCollection queryData)
{
var array = (from key in queryData.AllKeys
from value in queryData.GetValues(key)
select string.Format(CultureInfo.InvariantCulture, "{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)))
.ToArray();
return "?" + string.Join("&", array);
}

How to create extension method for Enum type argument?

Following is my code to convert enum values to Dictionary.
public static Dictionary<string, string> EnumToDictionary<T>() where T : struct, IConvertible
{
var oResult = new Dictionary<string, string>();
if (typeof(T).IsEnum)
foreach (T oItem in Enum.GetValues(typeof(T)))
oResult.Add(oItem.ToString(), oItem.ToString());
return oResult;
}
and this is my enum
public enum MyEnum
{
Value1,
Value2,
value3
}
Currently I am calling that method like
var result=EnumToDictionary<MyEnum>();
but I need to use that method like
var result=MyEnum.EnumToDictionary();
or any other way like string extension methods.
In general your problem is connected with the fact that you want to create a generic extensions method (that's possible) but without any object reference sent as "this" parameter when calling such a method (that's not possible).
So using extension methods is not an option to achieve what you want.
You could do sth like this:
public static Dictionary<string, string> EnumToDictionary(this Enum #enum)
{
var type = #enum.GetType();
return Enum.GetValues(type).Cast<string>().ToDictionary(e => e, e => Enum.GetName(type, e));
}
But this would mean that you need to operate on a certain instance of enum class to call such an extension method.
Or you could do this in such a way:
public static IDictionary<string, string> EnumToDictionary(this Type t)
{
if (t == null) throw new NullReferenceException();
if (!t.IsEnum) throw new InvalidCastException("object is not an Enumeration");
string[] names = Enum.GetNames(t);
Array values = Enum.GetValues(t);
return (from i in Enumerable.Range(0, names.Length)
select new { Key = names[i], Value = (int)values.GetValue(i) })
.ToDictionary(k => k.Key, k => k.Value.ToString());
}
And then call it like this:
var result = typeof(MyEnum).EnumToDictionary();
You could write an extension method, something like:
public static IDictionary<string, string> ToDictionary(this Enum value)
{
var result = new Dictionary<string, string>();
foreach (var item in Enum.GetValues(value.GetType()))
result.Add(Convert.ToInt64(item).ToString(), item.ToString());
return result;
}
But to call such an extension method, you need to provide an instance of the required enum. E.g.
var dict = default(System.DayOfWeek).ToDictionary();

C# Extension Method for generic GetOnlyKeys from IEnumerable<KeyValuePair<int, string>>

I have an IEnumberable> and I want only the list of Keys but cast to the needed type (i.e. perhaps short and not int). This is used in a custom generic multi-select control the binds to but the database needs potientially 'short' to save.
public static IEnumerable<T> GetKeysOnly<T>(this IEnumerable<KeyValuePair<int, string>> values)
{
Dictionary<int, string> valuesDictionary = values.ToDictionary(i => i.Key, i => i.Value);
List<int> keyList = new List<int>(valuesDictionary.Keys);
// Returns 0 records cuz nothing matches
//List<T> results = keyList.OfType<T>().ToList();
// Throws exception cuz unable to cast any items
//List<T> results = keyList.Cast<T>().ToList();
// Doesn't compile - can't convert int to T here: (T)i
//List<T> results = keyList.ConvertAll<T>(delegate(int i) { return (T)i; });
throw new NotImplementedException();
}
public static IEnumerable<short> GetKeysOnly(this IEnumerable<KeyValuePair<int, string>> values)
{
Dictionary<int, string> valuesDictionary = values.ToDictionary(i => i.Key, i => i.Value);
List<int> keyList = new List<int>(valuesDictionary.Keys);
// Works but not flexable and requires extension method for each type
List<short> results = keyList.ConvertAll(i => (short)i);
return results;
}
Any advice how to make my generic extension method work?
Thanks!
You want to get only the keys converted to a short?
var myList = valuesDictionary.Select(x => (short)x.Key).ToList();
// A Dictionary can be enumerated like a List<KeyValuePair<TKey, TValue>>
If you want to go to any type, then you would do something like this:
public static IEnumerable<T> ConvertKeysTo<T>(this IEnumerable<KeyValuePair<int, string>> source)
{
return source.Select(x => (T)Convert.ChangeType(x.Key, typeof(T)));
// Will throw an exception if x.Key cannot be converted to typeof(T)!
}

Bitfield enum extension method to return dictionary of included values

Enums can sure be confusing. I am trying to create an extension method on the Enum type that will take a value and return the names of all the bits that match.
Given:
[Flags]
public enum PlanetsEnum
{
Mercury=1,
Venus=2,
Earth=4,
Mars=8,
Jupiter=16,
//etc....
}
I would like to create an extension method that return a dictionary of only the selected values. So if:
PlanetsEnum neighbors = PlanetsEnum.Mars | PlanetEnum.Venus; //10
IDictionary dict = neighbors.ToDictionary();
foreach (KeyValuePair<String, Int32> kvp in dict)
{
Console.WriteLine(kvp.Key);
}
/*
* Should Print:
* Mars
* Venus
*/
I would expect to see Mars & Venus written to the console but I am instead seeing all the values of PlanetEnum. Here is my extension method code:
public static IDictionary<string, Int32> ToDictionary(this Enum enumeration)
{
Type type = enumeration.GetType();
return Enum.GetValues(type).Cast<Int32>().ToDictionary(field => Enum.GetName(type, field));
}
Does anyone see what I am doing wrong? I know that Enum.GetValues is returning all fields of the enum type, how do I get it to only return fields of the enum instance?
Thanks a lot for any help,
Keith
Here's the LINQ-ified version of what you're trying to do. The key bit you're missing is checking the current value:
public static IDictionary<string, int> ToDictionary(this Enum enumeration)
{
int value = (int)(object)enumeration;
return Enum.GetValues(enumeration.GetType()).OfType<int>().Where(v => (v & value) == value).ToDictionary(v => Enum.GetName(enumeration.GetType(), v));
}
Edit:
A little explanation on why we do the (int)(object)enumeration cast:
Bitwise operators only work against integral types (like int) and boolean, but Enum is not directly cast-able to int. So we must downcast to an object and presume that it actually represents an int as the underlying type - the compiler will allow us to cast an object to an int. If the enumeration is not integer-based, this will throw a runtime exception.
Try the following. It only works for Enum's which are int32's
public static IDictionary<String, Int32> ToDictionary(this Enum enumeration)
{
var map = new Dictionary<string, int>();
var value = (int)(object)enumeration;
var type = enumeration.GetType();
foreach (int cur in Enum.GetValues(type))
{
if (0 != (cur & value))
{
map.Add(Enum.GetName(type, cur),cur);
}
}
return map;
}
public enum CustomerType
{
Standard = 0,
Trade = 1
}
ddCustomerTypes.DataSource = (new Domain.CustomerType()).ToDictionary();
ddCustomerTypes.DataBind();
public static class EnumExtensionMethods
{
public static Dictionary<string, object> ToDictionary(this Enum enumeration)
{
Array names = Enum.GetNames(enumeration.GetType());
Dictionary<string, object> dictionary = new Dictionary<string, object>();
foreach (string name in names)
{
dictionary.Add(name, typeof(Domain.CustomerType).GetField(name).GetRawConstantValue() );
}
return dictionary;
}
public static List<KeyValuePair<string, object>> ToList(this Enum enumObject)
{
return enumObject.ToDictionary().ToList();
}
}

Categories