I have a dictionary defined.
Dictionary<string, string> dataSource;
There is a function defined in such a way that this Dictionary will get filled atleast ones.
dataSource = l2ListText
.Zip(l2ListValue, (lText, lValue) => new { lText, lValue })
.ToDictionary(x => x.lValue, x => x.lText);
l2ListText and l2ListValue are List<String>
This filled in datasource dictionary serves as a datasource for a listbox.
And second time round, it checks if a listbox is empty, and when it is NOT empty, I want to append values to this dataSource and re-assign it as a datasource to the listbox again.
I am finding it difficult to achieve this, please help.
Main problem in your code is that you initialize Dictionary with each call. Thus, you will never achieve adding items, only populating it from l2Lists
This line Dictionary<string, string> dataSource; should be moved at class level to ensure you have same instance for each call to the method.
Modified code is below:
if (dataSource != null && lstbx_confiredLevel2List.Items.Count > 0)
{
dataSource.Add(l2ListValue[0], l2ListText[0]);
}
else {
dataSource = l2ListText .Zip(l2ListValue, (lText, lValue) => new { lText, lValue }) .ToDictionary(x => x.lValue, x => x.lText);
}
lstbx_confiredLevel2List.DataSource = dataSource;
lstbx_confiredLevel2List.DataTextField = "Value";
lstbx_confiredLevel2List.DataValueField = "Key";
lstbx_confiredLevel2List.DataBind();
Without the LINQ .Zip extension, you can also do:
dataSource = new Dictionary<string, string>();
int zipCount = Math.Min(l2ListText.Count, l2ListValue.Count);
for (int idx = 0; idx < zipCount; ++idx)
datasource.Add(l2ListValue[idx], l2ListText[idx]);
The for loop appends to your Dictionary<,>. If you run a similar for loop again, new values will be appended. Note that this will fail if the same "keys" (members from l2ListValue) are added again. If you want, you can overwrite existing members insted:
for (int idx = 0; idx < zipCount; ++idx)
datasource[l2ListValue[idx]] = l2ListText[idx];
Related
Trying to read a csv file, and take the first word in the stream, throw it in to a dictionary while the following words get added to a list in that dictionary.
However, I find that (during debugging) when, inside my loop I decide to clear my list, all of the values it had added to the dictionary previously also get cleared. I guess I am mistaken in assuming it makes a copy of the list, it is actually just referencing that same list? Should I be creating a new list with every iteration? Code below:
public class TestScript : MonoBehaviour {
// Use this for initialization
void Start() {
Dictionary<string, List<string>> theDatabase = new Dictionary<string, List<string>>();
string word;
string delimStr = ",.:";
char[] delimiter = delimStr.ToCharArray();
List<string> theList = new List<string>();
using (StreamReader reader = new StreamReader("testComma.csv")) {
while (true) {
//Begin reading lines
string line = reader.ReadLine();
if (line == null) {
break;
}
//Begin splitting lines, adding to array.
string[] split2 = line.Split(delimiter, StringSplitOptions.RemoveEmptyEntries);
//Loop to hold the first word in the stream
for(int i = 0; i <= 0; i++) {
word = split2[i];
//loop to hold the following words in to list.
for (int y = 1; y < split2.Length; y++) {
theList.Add(split2[y]);
}
//Add word/list combo in to the database
theDatabase.Add(word, theList);
//clear the list.
theList.Clear();
}
}
}
foreach (KeyValuePair<string, List<string>> pair in theDatabase) {
string keys;
List<string> values;
keys = pair.Key;
values = pair.Value;
print(keys + " = " + values);
}
}
}
The bottom foreach loop is just so I can see the results. Also, any critique is welcome in regards to how this is written, as I'm a beginner.
Yes, you're adding the same object to your dictionary.
You can just change :
theDatabase.Add(word, theList);
To :
theDatabase.Add(word, theList.ToList());
Method ToList() makes shallow copy of your List<T>
C# is pass by reference.
So, theList and the list in your Dictionary are the same object.
The simplest solution is to stop clearing your List and create a new one every time instead:
for(int i = 0; i <= 0; i++) {
List<string> theList = new List<string>(); // it is in a loop now
word = split2[i];
//loop to hold the following words in to list.
for (int y = 1; y < split2.Length; y++) {
theList.Add(split2[y]);
}
//Add word/list combo in to the database
theDatabase.Add(word, theList);
//clear the list.
//theList.Clear(); - not required anymore
}
It is more readable and clear solution: create a list, insert items, paste a list into the dictionary, continue the iteration.
It is also much more performant since there is no List clearing - List<T>.Clear() is a linear operation, which takes O(n) operations.
Yes, as everyone says, lists are reference types. You need to make a copy to avoid the .Clear() clearing all the lists.
You could always write your code like this:
void Start()
{
string delimStr = ",.:";
Dictionary<string, List<string>> theDatabase =
File
.ReadAllLines("testComma.csv")
.Select(line => line.Split(delimStr.ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
.ToDictionary(x => x[0], x => x.Skip(1).ToList());
/* foreach here */
}
}
This doesn't have the problem with the list references.
So, I hope this is simple. I'm coming up with a way to store disconnected entities (due to my case being quite peculiar), and for it to work, I'd like to create a Dictionary with those values inside a for loop.
But I'm getting "An item with the same key" has been added problem, which I do not know why.
I've tried the following:
Dictionary<int, EntityState> StateProduct = new Dictionary<int, EntityState>();
for (int s = 0; s < userProducts.Count; s++ ) //userProducts.Count had value of 3
{
StateProduct.Add(s, EntityState.Modified);
}
But I get the error:
In which:
I really really do not know what's going on..
Edit: Here is the complete code
var dbIboID = dbs.OrderDB.Where(x => x.OrderID == Order[0].OrderID).FirstOrDefault();
if(dbIboID.IboID != uid)
{
return false;
}
//2nd Step:
//2.0 Attach it. Yes I know it sets it as unchanged. But let me do the magic trick!!!
dbIboID.OrderProcess = Order.ToList(); //CHANGED
dbs.OrderDB.Attach(dbIboID);
//2.1 Extract original values from the database.
var originalProducts = dbs.OrderProcessDB.Where(x => x.OrderProcessID == Order[0].OrderProcessID).ToList();
var userProducts = Order.ToList();
//This is a dictionary which will be used to set all other entities with their correct states!
Dictionary<int, System.Data.Entity.EntityState> StateProduct = new Dictionary<int, System.Data.Entity.EntityState>();
//2.3 Find new added products. addedProducts = userProducts[key] - originalProducts[key]
if(userProducts.Count > originalProducts.Count)
{
for (int i = originalProducts.Count - 1; i < userProducts.Count; i++ )
{
StateProduct.Add(i, System.Data.Entity.EntityState.Added);
}
}
//2.3 Find Deleted products = originalProducts - userProducts. Do reverse of the addedProducts
else
{
for (int i = userProducts.Count - 1; i < originalProducts.Count; i++)
{
StateProduct.Add(i, System.Data.Entity.EntityState.Deleted);
}
}
//2.4 Find modified products modifiedProducts = [userProducts - addedProducts] different originalProducts
//This is not 100% fool proof. Because there will be times that I will always have a modification,
// when objects remained unchanged.
for (int s = 0; s < userProducts.Count; s++ )
{
StateProduct.Add(s, System.Data.Entity.EntityState.Modified);
}
//2.5 Painting Process:
for (int i = 0; i < dbIboID.OrderProcess.Count(); i++ )
{
dbs.DB.Entry(dbIboID.OrderProcess[i]).State = StateProduct[i];
}
The code as you have shown it should not produce that exception, because the dictionary was allocated immediately prior to the loop, and thus should be empty, and the items being added all are unique integers.
My guess is that the dictionary already had some values in it. If so, then using Add to set a value will throw an ArgumentException, since the value corresponding to that key can only be replaced, not added, for the Dictionary class only allows one value per key.
So, if you expect the dictionary not to already have a value for a key, and want an error exception to be thrown if it does, do:
StateProduct.Add(s, EntityState.Modified)
If you want to add or replace a value, do:
StateProduct[s] = EntityState.Modified;
I have a Dictionary. I've used a loop to add elements in the dictionary and changed them over time through code. How can I revert the elements back to their initial value?
My code:
//for adding to the dictionary
Dictionary<string, string> D = new Dictionary<string, string>();
for(i=0; i<DataSet1.Tables[0].Rows.Count; i++)
{
row = DataSet1.Tables[0].Rows[i];
id[i]=row["UserID"].ToString();
D.Add(id[i], "absent");
}
//for reassigning back to absent
for(int i =0; i<D.Count; i++) D[i.ToString()]="absent";
My reassigning loop instead of reassign seems to just add new elements. Initialy counted about 1000 and after the reassign loop it's about 3000.. P.S. Using c#
Please help
You can enumerate keys of your dictionary and set entries values:
foreach(string key in D.Keys.ToArray())
D[key] = "absent";
Also creation of dictionary can be simplified to (with help of Linq to DataSet):
var D = DataSet1.Tables[0].AsEnumerable()
.ToDictionary(r => r.Field<string>("UserID"),
r => "absent");
Give this a try:
foreach(var key in d.Keys.ToArray())
{
d[key] = "absent";
}
The .ToArray() is needed because the foreach loop will throw an error before the second iteration if you try to iterate on d.Keys directly.
I have some strange problem where all my string arrays has the same value in the List.
Here is my code:
List<string[]> map_data = new List<string[]>();
string[] map_data_array = new string[11];
for(int i = 0; i < 2000; i++)
{
map_data_array = PopulateDataFromFile(); // it returns different data every call
map_data.Add(map_data_array); // store to List
}
map_data_array has always different data, I've verified that by placing the break point there and I've checked it.
The problem is that map_data has the value of all elements the same. And this value is the data that comes from function PopulateDataFromFile when the i is 1999.
What I am doing wrong? :/
That only happens if you place the same array into the list. As you did not give the code to PopulateDataFromFile we can only guess what happens. Make sure that the function returns a seperate array created with new each time.
You need to process your data in chunks since PopulateDataFromFile(); looks to be returning all of its data in one go (or as much as the array can fit). Using an extension method, you could do something like this: -
List<string[]> map_data = new List<string[]>();
foreach (var batch in PopulateDataFromFile().Batch(11))
{
map_data.Add((batch.ToArray());
}
Extension method: -
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items, int batchSize)
{
return items.Select((item, inx) => new { item, inx })
.GroupBy(x => x.inx / batchSize)
.Select(g => g.Select(x => x.item));
}
PopulateDataFromFile() is returning a String array with the same values.
In the loop everytime you just change the address of map_data_array , so that's why always the value will get changed to the newer data obtained from the method call. Reinitialize the string array everytime will help. It should look something like this
for(int i = 0; i < 2000; i++)
{
string[] map_data_array = PopulateDataFromFile(); // it returns different data every call
map_data.Add(map_data_array); // store to List
}
or if its confusing for you can you make it simple by
for(int i = 0; i < 2000; i++)
{
map_data.Add(PopulateDataFromFile()); // store to List
}
I wanted to bind a dictionary to a datagridview. Unfortunately Dictionary does not implement the required interface, so instead a created a List>.
Essentially I want this to be bound to a datagridview with datagridviewcomboboxcolumns. With column 1 holding the Key and column 2 holding the value.
I've tried loads of variations, but I can't seem to get this right. I've tried binding to the columns, to individual cells, and to the datagridview itself. Does anybody know how to do this?
EDIT: To clarify it's not binding to the object that's the problem. It seems to binding to the List okay, for example, if I have 4 items in the List, then 4 rows are added, however the values are blank. This is the example code:
additionalMetadata1.dataGridView1.DataSource = animal.AdditionalMetaData;
foreach (DataGridViewRow row in additionalMetadata1.dataGridView1.Rows)
{
DataGridViewCustomComboCell cell = row.Cells[0] as DataGridViewCustomComboCell;
cell.DataSource = animal.AdditionalMetaData;
((DataGridViewCustomComboColumn)additionalMetadata1.dataGridView1.Columns[0]).DisplayMember = "Key";
((DataGridViewCustomComboColumn)additionalMetadata1.dataGridView1.Columns[0]).ValueMember = "Key";
((DataGridViewCustomComboColumn)additionalMetadata1.dataGridView1.Columns[0]).DataPropertyName = "Key";
}
Thanks.
You could use your Dictionary with the following Linq:
dataGridView.DataSource = (from d in dictionary
orderby d.Value
select new
{
d.Key,
d.Value
}).ToList();
This will create an anonymous object that will hold your Key and Value as properties. Be aware that the dictionary is by default not in a particular order.
Try like this
dataGridView1.ColumnCount = 2;
foreach (KeyValuePair<string, string> kvp in dict) {
string[] arow = new string[] { kvp.Key, kvp.Value };
dataGridView1.Rows.Add(arow);
}
or
dataGridView1.DataSource = dict.ToArray();
or
dataGridView1.DataSource = dict.Select((kv => new {
myKeys = kv.Key,
myValues = kv.Value
})).ToArray();