Dynamic Where condition - c#

I have a little problem :) I have a list where some arguments are duplicate. I must remove that. I can't use Distinct becouse i must only see at some fields (not all).I think it's great option to use lambda epxressions.
I have a declaration on my object who i working, not the same but the idea is similar.
var keys = new string[] {"column1", "column2"};
var repeatedValues = new object[] {5, 15};
var values = new List<Dictionary<string, object>>();
//MAKE FAKE DOUBLE
values.Add(new Dictionary<string, object> {
{ "column1", 5 }, { "column2", 15 }, { "column3", "test" },
{ "column4", "test1" } });
for (int i = 0; i < 10; i++)
{
values.Add(new Dictionary<string, object> {
{"column1", i}, {"column2", 10 + i}, "column3", "test"},
{"column4", "test1"}});
}
The keys columns always have the same lenght as repeatedValues - but is changed, some the lenghts is 1, other 2,3,5. Not more than 5
The keys is like primaryKeys on database table. It's quite similar. So we looking for duplicates in "primary keys columns" - i think is a good compare.
We see in that example duplicate is has value 5 in "column1" and values 15 in "column2". How i before say i must remove that, but before i must count repeated items.
I try do code like that (i know the func method awalys fail becouse (object)1 == (object)1 always return false, but it's example :
Expression expression = null;
for (int i = 0; i < keys.Length; i++)
{
Expression<Func<Dictionary<string, object>, bool>> exp = x => x[keys[i]] == repeatedValues[i];
if (expression == null)
expression = exp.Body;
else
expression = Expression.AndAlso(expression, exp.Body);
}
var parameterExpression = Expression.Parameter(typeof (Dictionary<string, object>), "x");
var lamba = Expression.Lambda<Func<Dictionary<string, object>, bool>>(expression, parameterExpression);
var res = lamba.Compile();
var counts = queryLis.Count(res);
But compilator give me a exception
variable 'x' of type 'System.Collections.Generic.Dictionary`2[System.String,System.Object]' referenced from scope '', but it is not defined
It's possible to do this at this way ?
(Not about exception) In other step maybe the expression be asking for example repeatedValues[i] (after for) and it will doesn't know what is it ?

You need to pass the same Expression.Parameter that the original expression is referencing.
Making a new Expression.Parameter with the same name isn't good enough.

I don't know why you're messing with Expression and the like. If I understood you correctly, you're essentially duplicating a relational database situation with each entry of values being a row of data where the column represents the field name. If that's the case, then you might as well take a page from the database book and work off of an index. After your first snippet, you can get at a cleaned-up list like so:
// testing for duplicates
List<Dictionary<string, object>> duplicates = new List<Dictionary<string, object>>();
List<string> index = new List<string>();
foreach (var value in values)
{
List<string> keyValues = new List<string>();
foreach (string key in keys)
{
keyValues.Add(value[key].GetHashCode().ToString());
}
string hash = string.Join(",", keyValues.ToArray());
if (index.Contains(hash))
duplicates.Add(value);
else
index.Add(hash);
}
var cleanList = values.Except(duplicates);
EDIT: altered example so that it puts the hash in the same column order each time.

Related

Expression with Dictionary

I need to create an expression that extract a specific item from a dictionary.
The topic is to put the name of the item to extract inside the Expression.
I'll try to explain it better with an example.
I have this dictionary:
Dictionary<string, object> myDictionary = new Dictionary<string, object>
{
{ "Name", "My first name" },
{ "Age", 42 }
};
I wrote this code to compose an Expression that works with IDictionary:
private Expression<Func<IDictionary<string, object>, string, object>> GetDictionaryExpression()
{
var dictionary = Expression.Parameter(typeof(IDictionary<string, object>), "dict");
var keyParam = Expression.Parameter(typeof(string));
var indexer = typeof(IDictionary<string, object>).GetProperty("Item");
var indexerExpr = Expression.Property(dictionary, indexer, keyParam);
return Expression.Lambda<Func<IDictionary<string, object>, string, object>>(indexerExpr, dictionary, keyParam);
}
Finally, I can write this code:
var x = GetDictionaryExpression();
var y = x.Compile().Invoke(myDictionary, "Name"); // My first name
This code works.
What I need is to create an expression that doesn't need the field "Name" at runtime, but put it inside the Expression.
In other words, something that will let me have something like this:
var x = GetNameFromDictionary(); // <== How to write this method that extract only the 'Name' value from the dictionary?
var y = x.Compile().Invoke(visionRecord); // My first name
Thank you in advance.

Get Values from dictionary present in List

I have a list like,
List<string> list = new List<string>();
list.Add("MEASUREMENT");
list.Add("TEST");
I have a dictionary like,
Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("BPGA", "TEST");
dict.Add("PPPP", "TEST");
dict.Add("RM_1000", "MEASUREMENT");
dict.Add("RM_2000", "MEASUREMENT");
dict.Add("CDMA", "TEST");
dict.Add("X100", "XXX");
Now, I want to get all matched data from dictionary based on list.
Means, all data from list match with dict value then get new dictionary with following mathched values
Is there any way to achieve this by using lambda expression?
I want result like this.
Key Value
"BPGA", "TEST"
"PPPP", "TEST"
"RM_1000", "MEASUREMENT"
"RM_2000", "MEASUREMENT"
"CDMA", "TEST"
Thanks in advance!
You should be using the dictionary like it is intended to be used i.e. a common key with multiple values for example:
Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>();
Then all you need to do when adding the values is:
dict.Add("TEST", new List<string>() { /*strings go in here*/ });
Then to get all the results from a key like:
List<string> testValues = dict["TEST"];
To make it safe however you should check that the key exists i.e.
if (dict.ContainsKey("TEST"))
{
//Get the values
}
Then to add values to a current key you go do something like:
dict["TEST"].Add("NewValue");
If you insist on keeping the same structure, although I do not recommend it, something like the following will work:
List<string> testKeys = new List<string>();
foreach (var pairs in dict)
{
if (pair.Value == "TEST")
{
testKeys.Add(pair.Key);
}
}
Or even the following LINQ statement:
List<string> testKeys = dict.Where(p => p.Value == "TEST").Select(p => p.Key).ToList();
For a generic query to find the ones from your list use:
List<string> values = dict.Where(p => list.Contains(p.Value)).ToList();

Can't store multiple items in IDictionary [duplicate]

This question already has an answer here:
How to store multiple items in IDictionary?
(1 answer)
Closed 6 years ago.
I have created this IDictionary:
IDictionary<string, string> trace = new Dictionary<string, string>();
my goal is use it for save the content of json deserialized. I save the content in the IDictionary like this:
var obj = JsonConvert.DeserializeObject<List<RootObject>>(responseText);
foreach (var item in obj)
{
trace["date"] = item.trace.details.date;
trace["type"] = item.trace.details.type;
}
now in the obj variable I have 180 elements, the foreach over all items available in obj. The problem is that in the trace dictionary for each iteration each item is replaced, so I get only the item of the last iteration. How can I save all items in the dictionary? A dictionary shouldn't push each item automatically in the next iteration, instead of replacing it?
As #Santosh pointed out, this is expected behavior. You could instead use a List<Dictionary<String,String>>
var traces = new List<Dictionary<string, string>>();
var obj = JsonConvert.DeserializeObject<List<RootObject>>(responseText);
foreach (var item in obj)
{
var trace = new Dictionary<String,String>();
trace["date"] = item.trace.details.date;
trace["type"] = item.trace.details.type;
...
traces.Add(trace);
}
Try:
IDictionary<string, string> trace = new Dictionary<string, IList<string>>();
trace.Add("date", new List<string>())
trace.Add("type", new List<string>())
var obj = JsonConvert.DeserializeObject<List<RootObject>>(responseText);
foreach (var item in obj)
{
trace["date"].Add(item.trace.details.date)
trace["type"].Add(item.trace.details.type)
}
Plz, feel free to improve this design.
This is not how dictionaries work in C#. As the name suggests it should be one key with one value. You don't have repeated entries in dictionaries, right?
What you're probably trying to do is add to values in each entry, so I'll suggest using Tuple, since I don't know the type of your json parsed data, I'll assume string for everything, but type really won't change anything here :
var list = new List<Tuple<string,string>>();
foreach (var item in obj)
{
list.Add(new Tuple<string, string>(item.trace.details.date, item.trace.details.type));
}
Now you'll reach each entry as list[i].Item1 for a date on a given i index and list[i].Item2 for a type on the same index.
Using LINQ, you can do this:
var obj = JsonConvert.DeserializeObject<List<RootObject>>(responseText);
var traces = obj.Select(item => new Dictionary<string, string> {
{ "date", item.trace.details.date },
{ "type", item.trace.details.type }
});
You will get an IEnumerable<Dictionary<string, string>>. There isn't really a simpler way to do what you ask other than to use a collection of dictionaries.
Perhaps you can use a Lookup<string, string>, this is a one-to-many dictionary. You can create one using ToLookup() extension method:
var dates = obj.Select(o => new { Key = "date", Value = o.trace.details.date });
var types = obj.Select(o => new { Key = "type", Value = o.trace.details.type });
var lookUp = dates.Concat(types).ToLookup(kv => kv.Key, kv => kv.Value);

Linq expression to join a list and a dictionary

I have a list of part numbers:
var parts = new List<string> {"part1", "part2", "part3"};
I also have a dictionary of quantities for these part numbers:
var quantities = new Dictionary<string, int> {{"part1", 45}, {"part3", 25}};
Given a delimiter of |, I need to arrange these values in a flat file like so:
SalesRep|part1|part2|part3
Mr. Foo|45||25
What I'd like to do is define a string that no matter what values are in parts and quantities, I can tack this on to the sales rep name to resemble the example above.
It seems like I should be able to do this with a string.Join() on an enumerable LINQ operation, but I can't figure out what statement will get me the IEnumerable<string> result from joining parts and quantities. It thought that would be a .Join(), but the signature doesn't seem right. Can someone enlighten me?
Something like this perhaps?
var partValues = parts.Select(x => quantities.ContainsKey(x) ? quantities[x] : 0);
Basically for each item in the parts list you either pick the value from your dictionary, or if it doesn't exist 0.
To make this a little more interesting you could define a generic extension method on IDictionary<T,U> that makes this a little more readable:
public static class DictionaryExtensions
{
public static U GetValueOrDefault<T,U>(this IDictionary<T, U> dict, T key)
{
if(dict.ContainsKey(key))
{
return dict[key];
}
return default(U);
}
}
Then you can simply write:
var partValues = parts.Select(quantities.GetValueOrDefault);
var parts = new List<string> { "part1", "part2", "part3" };
var quantities = new Dictionary<string, int> { { "part1", 45 }, { "part3", 25 } };
var result = string.Join("|",
from p in parts select quantities.ContainsKey(p)
? quantities[p].ToString() : "");

Item duplication problem

My goal is to add a insert new value to a column where my column values are as follows
100 * 100
150 * 150
200 * 200
200 * 200
I get the following error:
Item has already been added. Key in dictionary: '200 x 200' Key being added: '200 x 200'
For next code:
SortedList sortedList = new SortedList();
foreach (ListItem listItem in ddldimension.Items)
sortedList.Add(listItem.Text, listItem.Value);
if (!sortedList.ContainsKey(CommonUtilities.GetCustomString("DefaultValues", "defaultEmbedDimension1")))
sortedList.Add(CommonUtilities.GetCustomString("DefaultValues", "defaultEmbedDimension1"), "defaultEmbedDimension1");
if (!sortedList.ContainsKey(CommonUtilities.GetCustomString("DefaultValues", "defaultEmbedDimension2")))
sortedList.Add(CommonUtilities.GetCustomString("DefaultValues", "defaultEmbedDimension2"), "defaultEmbedDimension2");
if (!sortedList.ContainsKey(CommonUtilities.GetCustomString("DefaultValues", "defaultEmbedDimension3")))
sortedList.Add(CommonUtilities.GetCustomString("DefaultValues", "defaultEmbedDimension3"), "defaultEmbedDimension3");
From the error message you're getting, and from the documentation for SortedList:
In either case, a SortedList does not allow duplicate keys.
So it would appear that a SortedList isn't the right structure for you to be using in your application. Unfortunately, you've provided insufficient information to allow me to suggest something better.
SortedList does not allow adding duplicate keys. Use List<> (along with KeyValuePair for example) instead (eg. List<KeyValuePair<string, object>>).
Here is the solution for your code:
var list = new List<KeyValuePair<string, string>>();
foreach (var item in ddldimension.Items)
{
list.Add(new KeyValuePair<string, string>(item.Text, item.Value));
}
var defaultEmbedDimension1 = CommonUtilities.GetCustomString("DefaultValues", "defaultEmbedDimension1");
int index = list.FindIndex(k => k.Key == defaultEmbedDimension1); // If there is no such Key, it will be -1. If you want to find by Value, replace k.Key by k.Value
if (index >= 0)
{
list.Add(new KeyValuePair<string, string>(defaultEmbedDimension1, "defaultEmbedDimension1"));
}
In this way, you allow to keep duplicate keys in your structure. Note you invoke the same method twice. Initialize variable instead:
string defaultEmbedDimension1 = CommonUtilities.GetCustomString("DefaultValues", "defaultEmbedDimension1");
To populate list, you can alternatively use LINQ:
var list = ddldimensions.Items.Select(item => new KeyValuePair<string, string>(item.Text, item.Value)).ToList();
Read also: C# KeyValuePair Collection Hints at Dot Net Perls.
But if you decide to disallow duplicates and gently deal with them in SortedList, you can create an extension:
public static class SortedListExtensions
{
public static bool AddIfNotContains<K, V>(this IDictionary<K, V> dictionary, K key, V value)
{
if (!dictionary.ContainsKey(key))
{
dictionary.Add(key, value);
return true;
}
return false;
}
}
And use it as I did below, without throwing exception:
var sortedList = new SortedList<string, string>();
sortedList.Add("a", "b");
sortedList.AddIfNotContains("a", "b"); // Will not be added
sortedList.AddIfNotContains("b", "b"); // Will be added

Categories

Resources