I have a function that returns a Hashtable. The var dt gets a bunch of RevenueGroupIDs and ProductIDs from the database that map together in a 1-to-many structure. For example:
RevenueGroupID ProductID
1 312
1 313
1 315
2 317
2 319
3 401
3 410
3 411
3 415
The combination of these 2 numbers are always unique -- no repeats. The function builds a Hashtable dictionary of key-value pairs in which the key is always a RevenueGroupID and the value is a List<int> of all the ProductIDs for that RevenueGroupID. The problem is: each time a key-value pair is added, all previous key-value pairs get overwritten with the current one. So at the end, ALL key value pairs are identical to the final one. I have stepped through the code and verified that each key-value pair is correct and unique. I can't see any reason for the reset. I have looked suspiciously at "productIDs.Clear();", but I can't see why that would be messing up the hashtable.
public static Hashtable GetAllProductIDsInAllRevenueGroups()
{
var productIDs = new List<int>();
var ht = new Hashtable();
string sql = #" {my sql here}";
var dt = Utilities.GetDataTableForQuery(sql, null);
int counter = 0;
int revenueGroupID = 0;
int lastRevenueGroupID = 0;
foreach (DataRow row in dt.Rows)
{
revenueGroupID = Utilities.SafeInt(row["RevenueGroupID"]);
int productID = Utilities.SafeInt(row["ProductID"]);
if (revenueGroupID != lastRevenueGroupID && counter > 0)
{
ht.Add(lastRevenueGroupID, productIDs);
productIDs.Clear();
}
productIDs.Add(productID);
lastRevenueGroupID = revenueGroupID;
counter++;
}
ht.Add(lastRevenueGroupID, productIDs);
return ht;
}
This is because you keep adding productIDs list to a hash table without making a copy, and then clear the content:
ht.Add(lastRevenueGroupID, productIDs);
productIDs.Clear(); // This removes all entries from the item stored at the lastRevenueGroupID key
This means that the same object is added over and over again, so you end up with multiple copies of the list that has the content of the last entry.
An easy fix is to make a new list before adding it to hash table, like this:
ht.Add(lastRevenueGroupID, productIDs.ToList());
productIDs.Clear();
The problem is that you are only using one list instead of creating a new list for each item. Adding the list to the hash table doesn't create a copy of the list, it just adds the reference. When you clear the list you will clear the list for all previously added items in the hash table, because they are all the same list.
You can create a new list and add to the hash table when you start a new group. As you keep the reference to the list, you can keep adding numbers to it after it is places in the hash table:
public static Hashtable GetAllProductIDsInAllRevenueGroups()
{
var productIDs;
var ht = new Hashtable();
string sql = #" {my sql here}";
var dt = Utilities.GetDataTableForQuery(sql, null);
int counter = 0;
int revenueGroupID = 0;
int lastRevenueGroupID = 0;
foreach (DataRow row in dt.Rows)
{
revenueGroupID = Utilities.SafeInt(row["RevenueGroupID"]);
int productID = Utilities.SafeInt(row["ProductID"]);
if (counter == 0 || revenueGroupID != lastRevenueGroupID)
{
productIDs = new List<int>();
ht.Add(revenueGroupID, productIDs);
}
productIDs.Add(productID);
lastRevenueGroupID = revenueGroupID;
counter++;
}
return ht;
}
Note: Consider using the strictly typed Dictionary<int, List<int>> instead of Hashtable.
Related
I have below code which adds Dictionary data to List of IDictionary as shown below
var datalist = new List<IDictionary<string, string>>();
var data = new Dictionary<string, string>();
for (var i = 0; i < dataTable.Rows.Count; ++i)
{
foreach (var name in arrColumnNames)
{
data[name] = Convert.ToString(dataTable.Rows[i][name]);
}
datalist.Add(data);
}
Now, issue is that my datalist keeps on updating last data values in entire List of datalist. What's wrong? How to keep data values preserved in datalist?
My Spidey Senses tells me you are new'ing this up for every method call, In short, the problem seems like you are creating a new list every time. However i could be completely wrong
Try this in your class, and remove it from the method
Class field
private List<IDictionary<string, string>> datalist = new List<IDictionary<string, string>>();
Method body
Assuming your arrColumnNames is an instantiated List of string
You could do this with Linq
var dict = dataTable.Rows
.Cast<DataColumnCollection>()
.Select(row => arrColumnNames.ToDictionary(x => x, x => row[x].ToString()))
.ToList();
datalist.AddRange(dict);
Footnote : i think this is all a little-bit suspect. Are you sure you want a Dictionary in a List, and not a Dictionary of Key and List
I think what you can do is get a shallow copy of the dictionary after you add the data to the list, try this
var datalist = new List<IDictionary<string, string>>();
var data = new Dictionary<string, string>();
for (var i = 0; i < dataTable.Rows.Count; ++i)
{
foreach (var name in arrColumnNames)
{
data[name] = Convert.ToString(dataTable.Rows[i][name]);
}
datalist.Add(data);
data = new Dictionary<string, string>(data);
}
var datalist = new List<IDictionary<string, string>>();
for (var i = 0; i < dataTable.Rows.Count; ++i)
{
var data = new Dictionary<string, string>();
foreach (var name in arrColumnNames)
{
data[name] = Convert.ToString(dataTable.Rows[i][name]);
}
datalist.Add(data);
}
Moved Dictionary Declaration inside for loop ,so that each data table it creates new object of dictionary and add all the Columns. Otherwise Dictionary keeps last datable Column details
You are only creating a single instance of a Dictionary (outside the for loop), and adding the same instance of it for each row inside the loop.
As it stands, your list will end up being a list of references to the same dictionary, and since the final row in the table has the same columns as all the other rows, you end up with what would look like a list of dictionaries all with the values from the last row. But in fact it's a list where each entry points at the same dictionary, which only contains values from the last row because it has overwritten all the previous values.
If you move the line initializing data inside the for loop, you will end up with a list (one entry for each row) of dictionaries where the key is the column name and the value is the value for that column for that row.
You have to move
var data = new Dictionary<string, string>();
Into
for (var i = 0; i < dataTable.Rows.Count; ++i)
Before the foreach loop.
I am working on a legacy project which was developed using .NET Framework 2.0.
In this project, I get distinct values from DataRowCollection by ItemNo column. I am only interested in ItemNo. The DataRow consist of ItemNo, Qty and Date.
I am thinking of iterating the DataRowCollection and adding the unique ItemNo into a list of string as below (not tested)
var items = new List<string>();
foreach (DataRow orderItem in rows)
{
var itemNo = orderItem["ITEMNO"].ToString().Trim();
if(items.Find(delegate(string str) { return str == itemNo ;}) == null)
{
items.Add(itemNo);
}
}
Is there a better way of doing this without LINQ (.Net Framework 2.0 doesnt like LINQ)
// Given a data table:
var dt = new DataTable();
dt.Columns.Add("ITEMNO");
dt.Rows.Add("1 ");
dt.Rows.Add(" 1");
dt.Rows.Add("2");
var dict = new Dictionary<string, bool>();
foreach(DataRow dr in dt.Rows)
{
var itemNo = dr["ITEMNO"].ToString().Trim();
// Take advantage of O(1) lookup:
if (!dict.ContainsKey(itemNo))
{
dict.Add(itemNo, true);
}
}
// Get list from dictionary keys:
var items = new List<string>(dict.Keys);
If you can install .Net 3.5 on the server, and reference System.Core.dll in your application, you can leverage HashSets which would modify the above code to:
var hashSet = new HashSet<string>();
foreach(DataRow dr in dt.Rows)
{
var itemNo = dr["ITEMNO"].ToString().Trim();
// Only unique elements are added to the hash set,
// no need to check for duplicates
hashSet.Add(itemNo);
}
var items = new List<string>(hashSet);
The benefit of using HashSet over a Dictionary is admittedly trivial, but I'd prefer it since I don't care for the arbitrary bool value in the dictionary, but you'd need to meet the .Net 3.5 and reference requisites.
To get distinct values form a column you can use this method:
List<T> SelectDistict<T>(DataTable table, string column)
{
DataTable temp = new DataView(table).ToTable(true, column);
List<T> items = new List<T>();
foreach (DataRow row in temp.Rows)
items.Add(row.Field<T>(column));
return items;
}
In above method I used DataView.ToTable which by passing true as first argument, selects distinct values.
Here is the usage example:
List<string> items = SelectDistict<string>(yourDataTable, "ITEMNO");
Note
If you need to trim values, you can change above code and first create a clone copy of the DataTable. Then add a computed column which contains, trimmed value from the given column name for distinct values by assigning TRIM(column) to Expression property of column. Then follow the steps using the new trimmed column like above code.
I need to save those six fields in same column but not in same row and same cell. each field have default GUID.so i decided to put that default guid's in one list and fields in one list and call that object of that particular list where we want .
ArrayList Alist = new ArrayList();
{
Alist.Add("FD713788-B5AE-49FF-8B2C-F311B9CB0CC4");
Alist.Add("64B512E7-46AE-4989-A049-A446118099C4");
Alist.Add("376D45C8-659D-4ACE-B249-CFBF4F231915");
Alist.Add("59A2449A-C5C6-45B5-AA00-F535D83AD48B");
Alist.Add("03ADA903-D09A-4F53-8B67-7347A08EDAB1");
Alist.Add("2F405521-06A0-427C-B9A3-56B8931CFC57");
}
ArrayList objValue = new ArrayList();
{
objValue.Add(viewmodel.TinNo);
objValue.Add(viewmodel.CstNo);
objValue.Add(viewmodel.PanNo);
objValue.Add(viewmodel.CinNo);
objValue.Add(viewmodel.ExciseRegNo);
objValue.Add(viewmodel.ServiceTaxNo);
}
var TaxInfoTaxFiledclassobj = new TaxInfoTaxFiled()
{
TaxInfoTaxFieldID = TaxInfoTaxFieldObj,
TaxFieldID = new Guid(Alist .ToString ()),
FieldValue = objValue.ToString(),
};
All are working Fine
but in TaxFieldID it show the count which has been calculated from list but while saving it show the below error
What shall I do?
You're trying to pass an ArrayList as a Guid. In this line:
TaxFieldID = Guid.Parse(Alist.ToString())
You need to select just one of the elements of the ArrayList to parse. Additionally, you could use a List<Guid> to eliminate the problem altogether.
List<Guid> guidList = new List<Guid>();
guidList.Add(new Guid("DDE4BA55-808E-479F-BE8B-72F69913442F"));
...
TaxFieldID = guidList[0]; // obviously, select the appropriate GUID
Guid.Parse() is capable of parsing a GUID. Alist.ToString() should not be a GUID.
EDIT
I guess you're looking for something like this -
var listFiled = new List<TaxInfoTaxFiled>();
for(var item = 0; item < objValue.Count ; item++)
{
listFiled.Add(new TaxInfoTaxFiled
{
TaxInfoTaxFieldID = TaxInfoTaxFieldObj,
TaxFieldID = new Guid(Alist[item]),
FieldValue = objValue[item]
});
}
AList is an array, not a Guid.
You must be doing Guid.Parse on entries of AList
Something like
foreach(string g in AList)
{
Guid guid = Guid.Parse(g);
// Guid guid = new Guid(g) also works
}
I have something like this:
Dictionary<int, List<string>> fileList = new Dictionary<int, List<string>>();
and then, I fill it with some variables, for example:
fileList.Add(
counter,
new List<string> {
OFD.SafeFileName,
OFD.FileName,
VERSION, NAME , DATE ,
BOX , SERIAL_NUM, SERIES,
POINT , NOTE , VARIANT
}
);
Where counter is a variable that increment +1 each time something happens, List<string>{XXX} where XXX are string variables that holds some text.
My question is, how do I access these strings from the list, if counter == 1?
You can access the data in the dictionary and lists just like normal. Remember, access a value in the dictionary first, which will return a list. Then, access the items in the list.
For example, you can index into the dictionary, which returns a list, and then index into the list:
------ Returns a list from the dictionary
| --- Returns an item from the list
| |
v v
fileList[0][0] // First item in the first list
fileList[1][0] // First item in the second list
fileList[1][1] // Second item in the second list
// etc.
FishBasketGordo explains how you can access entries in your data structure. I will only add some thoughts here:
Dictionaries (based on hash tables) allow fast access to arbitrary keys. But your keys are given by a counter variable (counter = 0, 1, 2, 3, 4 ...). The fastest way to access such keys is to simply use the index of an array or of a list. Therefore I would just use a List<> instead of a Dictionary<,>.
Furthermore, your list seems not to list anonymous values but rather values having very specific and distinct meanings. I.e. a date is not the same as a name. In this case I would create a class that stores these values and that allows an individual access to individual values.
public class FileInformation
{
public string SafeFileName { get; set; }
public string FileName { get; set; }
public decimal Version { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
...
}
Now you can create a list like this:
var fileList = new List<FileInformation>();
fileList.Add(
new FileInformation {
SafeFileName = "MyDocument.txt",
FileName = "MyDocument.txt",
Version = 1.2,
...
}
}
And you can access the information like this
decimal version = fileList[5].Version;
If the keys don't start at zero, just subtract the starting value:
int firstKey = 100;
int requestedKey = 117;
decimal version = fileList[requestedKey - firstKey].Version;
Dictionary uses Indexer to access its vallues via key.
List<string> items = fileList[counter];
var str0 = items[0];
var str1 = items[1];
Then you can do anything with the list.
Dictionary<int, List<string>> fileList = new Dictionary<int, List<string>>();
fileList.Add(101, new List<string> { "fijo", "Frigy" });
fileList.Add(102, new List<string> { "lijo", "liji" });
fileList.Add(103, new List<string> { "vimal", "vilma" });
for (int Key = 101; Key < 104; Key++)
{
for (int ListIndex = 0; ListIndex < fileList[Key].Count; ListIndex++)
{
Console.WriteLine(fileList[Key][ListIndex] as string);
}
}
You can access the List through MyDic[Key][0]. While editing the list, there won't be any run time errors, however it will result in unnecessary values stored in Dictionary. So better:
assign the MyDict[Key] to new list
edit the new list and then
reassign the new list to MyDict[Key] rather than editing a
particular variable in the Dictionary with List as Values.
Code example:
List<string> lstr = new List<string(MyDict[Key]);
lstr[0] = "new Values";
lstr[1] = "new Value 2";
MyDict[Key] = lstr;
I want to find all rows in a DataTable where each of a group of columns is a duplicate. My current idea is to get a list of indexes of all rows that appear more than once as follows:
public List<int> findDuplicates_New()
{
string[] duplicateCheckFields = { "Name", "City" };
List<int> duplicates = new List<int>();
List<string> rowStrs = new List<string>();
string rowStr;
//convert each datarow to a delimited string and add it to list rowStrs
foreach (DataRow dr in submissionsList.Rows)
{
rowStr = string.Empty;
foreach (DataColumn dc in submissionsList.Columns)
{
//only use the duplicateCheckFields in the string
if (duplicateCheckFields.Contains(dc.ColumnName))
{
rowStr += dr[dc].ToString() + "|";
}
}
rowStrs.Add(rowStr);
}
//count how many of each row string are in the list
//add the string's index (which will match the row's index)
//to the duplicates list if more than 1
for (int c = 0; c < rowStrs.Count; c++)
{
if (rowStrs.Count(str => str == rowStrs[c]) > 1)
{
duplicates.Add(c);
}
}
return duplicates;
}
However, this isn't very efficient: it's O(n^2) to go through the list of strings and get the count of each string. I looked at this solution but couldn't figure out how to use it with more than 1 field. I'm looking for a less expensive way to handle this problem.
Try this:
How can I check for an exact match in a table where each row has 70+ columns?
The essence is to make a set where you store hashes for rows and only do comparisons between rows with colliding hashes, complexity will be O(n)
...
If you have a large number of rows and storing the hashes themselves is an issue (an unlikely case, but still...) you can use a Bloom filter. The core idea of a Bloom filter is to calculate several different hashes of each row and use them as an address in a bitmap. As you're scanning through the rows you can double-check the rows that already have all the bits in the bitmap previously set.