I am getting a datatable with customer data from a MySql databaes and a customer object from a web service.
I want to compare every value in the datatable with the values in the object and if there is one field that differs I want to perfrom some tasks.
I know I can get the values from the datatable with:
string mCompanyName = row["Company Name"].ToString();
string mCreatedDate = row["Created Date"].Tostring();
//etc..
Then I get the values from the web service
string wsCompanyName = customer.companyName;
string wsCreatedDate = customer.createdDate;
There are about 50 fields and doing
if( mCompanyName != wsCompanyName & mCreatedDate != wsCreatedDate and so on..) (or similar)
{
//Do something
}
seems to be a bit tedious and not very nice so how should I perform this? Is there a much better way to chuck it into a list and use some fancy LINQ?
Thanks in advance.
For cases like this I sometimes put them ("the objects") in something IEnumerable (make sure to "line them up") and use the SequenceEqual extension method. It performs standard Equals()'ity and is "cheap enough for my usage".
For instance:
var equal = (new object[] { row["A"], row["B"] })
.SequenceEqual(new object[] { x.A, x.B });
This requires LINQ, of course.
I'd put them in a Dictionary and search that way:
Dictionary<string, string> mData = new Dictionary<string, string>();
mData.Add("Company Name", row["Company Name"].ToString());
Dictionary<string, string> wsData = new Dictionary<string, string>();
wsData.Add("Company Name", customer.CompanyName);
Then loop through:
foreach (KeyValuePair<string, string> pair in mData)
{
if (wsData[pair.Key] == pair.Value)
{
// Do something
}
}
This way, for every entry in mData (the data from your database), it will look for an entry in wsData with the same name.
I wouldn't create individual variables for each piece of data. It would be difficult to maintain, and would not scale well (lots of copy and pastes).
I think this might help you, but it needs to modify this to use in your scenerio stackoverflow link
Related
In a program that I've been working on, there are three steps to get the data into a Dictionary that's been created:
execute the SQL command
pull those results into a DataTable, then
pull the DataTable into the Dictionary
Code:
var myDr = myLookup.ExecuteReader();
dt.Load(myDr);
customerLookup = dt.AsEnumerable()
.ToDictionary(key => key.Field<string>("code"),
value => value.Field<string>("customerText"));
My question is, is it possible to “cut out the middleman,” so to speak, and pull the data from the SqlDataReater directly into the Dictionaries? Or is it necessary to pull it into a DataTable first? If what I'm looking to do is possible, can someone please post code for me to try?
Thanks very much!
You can just loop through the rows returned by the reader:
var customerLookup = new Dictionary<string, string>();
using (var reader = myLookup.ExecuteReader())
{
while (reader.Read())
{
customerLookup[(string)reader["code"]] = (string)reader["customerText"];
}
}
You should be aware that if there are any duplicate codes, subsequent code values will overwrite previous ones in the dictionary. You can use customerLookup.Add() instead if you'd rather an exception be thrown in such a case.
Not only can you, but you definitely should. The code, as you show it, shows a complete lack of knowledge of how .NET works.
Some of the code I am suggesting may be considered "overkill" for the question at hand, but it does demonstrate some best practices.
Dictionary<string, string> customerLookup = new Dictionary<string, string>();
using (var reader = myLookup.ExecuteReader())
{
int ordinalCode = reader.GetOrdinal("code");
int ordinalCustomerText = reader.GetOrdinal("customerText");
while (reader.Read())
{
//this code assumes the values returned by the reader cannot be null
customerLookup.Add(reader.GetString(ordinalCode), reader.GetString(ordinalCustomerText))
}
}
Yes, it's possible. You should use SqlDataReader.Read method.
My data source could have duplicate keys with values.
typeA : 1
typeB : 2
typeA : 11
I chose to use NameValueCollection as it enables entering duplicate keys.
I want to remove specific key\value pair from the collection, but NameValueCollection.Remove(key) removes all values associated with the specified key.
Is there a way to remove single key\value pair from a NameValueCollection,
OR
Is there a better collection in C# that fits my data
[EDIT 1]
First, thanks for all the answers :)
I think I should have mentioned that my data source is XML.
I used System.Xml.Linq.XDocument to query for type and also it was handy to remove a particular value.
Now, my question is, for large size data, is using XDocument a good choice considering the performance?
If not what are other alternatives (maybe back to NameValueCollection and using one of the techniques mentioned to remove data)
The idea of storing multiple values with the same key is somehow strange. But I think you can retrieve all values using GetValues then remove the one you don't need and put them back using Set and then subsequent Add methods. You can make a separate extension method method for this.
NameValueCollection doesn't really allow to have multiple entries with the same key. It merely concatenates the new values of existing keys into a comma separated list of values (see NameValueCollection.Add.
So there really is just a single value per key. You could conceivably get the value split them on ',' and remove the offending value.
Edit: #ElDog is correct, there is a GetValues method which does this for you so no need to split.
A better option I think would be to use Dictionary<string, IList<int>> or Dictionary<string, ISet<int>> to store the values as discrete erm, values
You may convert it to Hashtable
var x = new NameValueCollection();
x.Add("a", "1");
x.Add("b", "2");
x.Add("a", "1");
var y = x.AllKeys.ToDictionary(k => k, k=>x[k]);
make your own method, it works for me --
public static void Remove<TKey,TValue>(
this List<KeyValuePair<TKey,TValue>> list,
TKey key,
TValue value) {
return list.Remove(new KeyValuePair<TKey,TValue>(key,value));
}
then call it on list as --
list.Remove(key,value); //Pass the key value...
Perhaps not the best way, but....
public class SingleType
{
public string Name;
public int Value;
}
List<SingleType> typeList = new List<SingleType>();
typeList.Add (new SingleType { Name = "TypeA", Value = 1 });
typeList.Add (new SingleType { Name = "TypeA", Value = 3 });
typeList.Remove (typeList.Where (t => t.Name == "TypeA" && t.Value == 1).Single());
You can use the Dictionary collection instead:
Dictionary<string, int> dictionary = new Dictionary<string, int>();
dictionary.Add("typeA", 1);
dictionary.Add("typeB", 1);
When you try to insert type: 11 it will throw exception as Key already exists. So you can enter a new key to insert this data.
Refer this Tutorial for further help.
I am drawing a blank here.. I am using MVC and my model doesn’t have the Value Name, it just has the ID.
The problem is with ‘itm.DegreeTypeId’ it is just an int. But I need to match it up with a string name.. so DegreeTypeId 1 = Associates Degree, 2 = Bachelor, 3 = MBA etc.
I don’t want to update the model to have the name, but instead to a quick lookup. What’s the best way to proceed here? I know I can create a method and have it return the string based on a number but there has to be a better cleaner way.
string education = string.Empty;
int eduCount = 0;
foreach (var itm in candidate.Educations)
{
if (eduCount > 0) education += "<br><br>";
education += string.Format("<b>{0}</b><br>{1} {2}<br>{3}Graduated: {4}<br>",
itm.DegreeTypeId,
itm.InstitutionName,
itm.InstitutionLocation,
itm.GraduatedOn.HasValue
? string.Format("{0:MMMM yyyy}", itm.GraduatedOn.Value)
: string.Empty);
eduCount++;
}
Try using a Dictionary
static readonly Dictionary<int, string> Degrees = new Dictionary<int, string>() {
{1, "Associates Degree"},
{2, "Bachelor"},
{3, "MBA"},
...
};
So the relevant snippet looks like this:
education += string.Format("<b>{0}</b><br>{1} {2}<br>{3}Graduated: {4}<br>",
Degrees(itm.DegreeTypeId),
...
It's not entirely clear what you're after, but a Dictionary<int, string> could work (or the reverse if you're trying to look things up the other way). Even a string[] could be a simple solution for int to string conversion:
static readonly string[] DegreeTypeNames = { null, // Unused
"Associates Degree",
"Bachelor",
"MBA"
};
That's useful (and performs really well) if you've got a lot of consecutive values, but if they're not all consecutive, the dictionary approach would be better.
If you need to refer to these IDs in code, you should potentially consider using an enum.
just messing around, trying to expand my bag o' tricks: I was just experimenting and want to do something like a Dictionary object with another inner Dictionary as the outside Dictionary's .Value
var dictionary = new Dictionary<ObjectType, Dictionary<string, string>>();
ObjectType is an enum
so...what then...either you're not suppose to do this or I just don't know how 'cause I started running into a wall when I was trying to figure out how to populate and retrieve data from it.
Purpose might help: I'm being passed an ObjectGUID and need to flip through a bunch of database tables to determine which table the object exists in. The method I've already written just queries each table and returns count (here are a couple examples)
// Domain Check
sql = string.Format(#"select count(domainguid) from domains where domainguid = ?ObjectGUID");
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return ObjectType.Domain;
// Group Check
sql = string.Format(#"select count(domaingroupguid) from domaingroups where domaingroupguid = ?ObjectGUID");
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return ObjectType.Group;
So, that's all done and works fine...but because the fieldname and table name are the only things that change for each check I started thinking about where I could re-use the repetitive code, I created a dictionary and a foreach loop that flips through and changes the sql line (shown below)...but, as you can see below, I need that ObjectType as kind of the key for each table/fieldname pair so I can return it without any further calculations
Dictionary<string, string> objects = new Dictionary<string,string>();
objects.Add("domains", "domainguid");
objects.Add("domaingroups", "domaingroupguid");
objects.Add("users", "userguid");
objects.Add("channels", "channelguid");
objects.Add("categorylists", "categorylistguid");
objects.Add("inboundschemas", "inboundschemaguid");
objects.Add("catalogs", "catalogguid");
foreach (var item in objects)
{
sql = string.Format(#"select count({0}) from {1} where {0} = ?ObjectGUID", item.Value, item.Key);
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return ?????
}
This isn't all that important since my original method works just fine but I thought you StackOverflow geeks might turn me on to some new clever ideas to research...I'm guessing someone is going to smack me in the head and tell me to use arrays... :)
EDIT # Jon Skeet ------------------------------------------
Heh, sweet, think I might have come upon the right way to do it...haven't run it yet but here's an example I wrote for you
var objectTypes = new Dictionary<string, string>();
objectTypes.Add("domainguid", "domains");
var dictionary = new Dictionary<ObjectType, Dictionary<string, string>>();
dictionary.Add(ObjectType.Domain, objectTypes);
foreach(var objectType in dictionary)
{
foreach(var item in objectType.Value)
{
sql = string.Format(#"select count({0}) from {1} where {0} = ?ObjectGUID", item.Key, item.Value);
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return objectType.Key;
}
}
This chunk should hit the domains table looking for domainguid and if count > 0 return ObjectType.Domain...look right? Only problem is, while it might seem somewhat clever, it's like 2 dictionary objects, a couple strings, some nested loops, harder to read and debug than my first version, and about 10 more lines per check hehe...fun to experiment though and if this looks like to you then I guess it's one more thing I can add to my brain :)
also found this how to fetch data from nested Dictionary in c#
You can definitely do it, although you're currently missing a closing angle bracket and parentheses. It should be:
var dictionary = new Dictionary<ObjectType, Dictionary<string, string>>().
To add a given value you probably want something like:
private void AddEntry(ObjectType type, string key, string value)
{
Dictionary<string, string> tmp;
// Assume "dictionary" is the field
if (!dictionary.TryGetValue(type, out tmp))
{
tmp = new Dictionary<string, string>();
dictionary[type] = tmp;
}
tmp.Add(key, value);
}
If that doesn't help, please show the code that you've tried and failed with - the database code in your question isn't really relevant as far as I can tell, as it doesn't try to use a nested dictionary.
i have an array of custom objects. i'd like to be able to reference this array by a particular data member, for instance myArrary["Item1"]
"Item1" is actually the value stored in the Name property of this custom type and I can write a predicate to mark the appropriate array item. However I am unclear as to how to let the array know i'd like to use this predicate to find the array item.
I'd like to just use a dictionary or hashtable or NameValuePair for this array, and get around this whole problem but it's generated and it must remain as CustomObj[]. i'm also trying to avoid loading a dictionary from this array as it's going to happen many times and there could be many objects in it.
For clarification
myArray[5] = new CustomObj() // easy!
myArray["ItemName"] = new CustomObj(); // how to do this?
Can the above be done? I'm really just looking for something similar to how DataRow.Columns["MyColumnName"] works
Thanks for the advice.
What you really want is an OrderedDictionary. The version that .NET provides in System.Collections.Specialized is not generic - however there is a generic version on CodeProject that you could use. Internally, this is really just a hashtable married to a list ... but it is exposed in a uniform manner.
If you really want to avoid using a dictionary - you're going to have to live with O(n) lookup performance for an item by key. In that case, stick with an array or list and just use the LINQ Where() method to lookup a value. You can use either First() or Single() depending on whether duplicate entries are expected.
var myArrayOfCustom = ...
var item = myArrayOfCustom.Where( x => x.Name = "yourSearchValue" ).First();
It's easy enough to wrap this functionality into a class so that external consumers are not burdened by this knowledge, and can use simple indexers to access the data. You could then add features like memoization if you expect the same values are going to be accessed frequently. In this way you could amortize the cost of building the underlying lookup dictionary over multiple accesses.
If you do not want to use "Dictionary", then you should create class "myArrary" with data mass storage functionality and add indexers of type "int" for index access and of type "string" for associative access.
public CustomObj this [string index]
{
get
{
return data[searchIdxByName(index)];
}
set
{
data[searchIdxByName(index)] = value;
}
}
First link in google for indexers is: http://www.csharphelp.com/2006/04/c-indexers/
you could use a dictionary for this, although it might not be the best solution in the world this is the first i came up with.
Dictionary<string, int> d = new Dictionary<string, int>();
d.Add("cat", 2);
d.Add("dog", 1);
d.Add("llama", 0);
d.Add("iguana", -1);
the ints could be objects, what you like :)
http://dotnetperls.com/dictionary-keys
Perhaps OrderedDictionary is what you're looking for.
you can use HashTable ;
System.Collections.Hashtable o_Hash_Table = new Hashtable();
o_Hash_Table.Add("Key", "Value");
There is a class in the System.Collections namespace called Dictionary<K,V> that you should use.
var d = new Dictionary<string, MyObj>();
MyObj o = d["a string variable"];
Another way would be to code two methods/a property:
public MyObj this[string index]
{
get
{
foreach (var o in My_Enumerable)
{
if (o.Name == index)
{
return o;
}
}
}
set
{
foreach (var o in My_Enumerable)
{
if (o.Name == index)
{
var i = My_Enumerable.IndexOf(0);
My_Enumerable.Remove(0);
My_Enumerable.Add(value);
}
}
}
}
I hope it helps!
It depends on the collection, some collections allow accessing by name and some don't. Accessing with strings is only meaningful when the collection has data stored, the column collection identifies columns by their name, thus allowing you to select a column by its name. In a normal array this would not work because items are only identified by their index number.
My best recommendation, if you can't change it to use a dictionary, is to either use a Linq expression:
var item1 = myArray.Where(x => x.Name == "Item1").FirstOrDefault();
or, make an extension method that uses a linq expression:
public static class CustomObjExtensions
{
public static CustomObj Get(this CustomObj[] Array, string Name)
{
Array.Where(x => x.Name == Name).FirstOrDefault();
}
}
then in your app:
var item2 = myArray.Get("Item2");
Note however that performance wouldn't be as good as using a dictionary, since behind the scenes .NET will just loop through the list until it finds a match, so if your list isn't going to change frequently, then you could just make a Dictionary instead.
I have two ideas:
1) I'm not sure you're aware but you can copy dictionary objects to an array like so:
Dictionary dict = new Dictionary();
dict.Add("tesT",40);
int[] myints = new int[dict.Count];
dict.Values.CopyTo(myints, 0);
This might allow you to use a Dictionary for everything while still keeping the output as an array.
2) You could also actually create a DataTable programmatically if that's the exact functionality you want:
DataTable dt = new DataTable();
DataColumn dc1 = new DataColumn("ID", typeof(int));
DataColumn dc2 = new DataColumn("Name", typeof(string));
dt.Columns.Add(dc1);
dt.Columns.Add(dc2);
DataRow row = dt.NewRow();
row["ID"] = 100;
row["Name"] = "Test";
dt.Rows.Add(row);
You could also create this outside of the method so you don't have to make the table over again every time.