Modifying source items changes list items? [duplicate] - c#

This question already has answers here:
Issue with List.Add() only saving the last added item [duplicate]
(2 answers)
Closed 4 years ago.
I'm coding a little plugin for a software using Windows Forms in C#.
I need to parse an XML file to retrieve some objects and add them into a ListBox. The problem is that at the end of my program, all objects are the same as the last one added.
I kind of figured out why, but I'm still searching how to resolve it. Here is a little example with a String[] instead of my objects:
static void Main(string[] args)
{
ListBox listbox = new ListBox();
String[] s = new string[] { "5", "2", "3" };
listbox.Items.Add(s);
s[2] = "0";
listbox.Items.Add(s);
Console.WriteLine(((String[])listbox.Items[0])[2]); // result => 0
Console.WriteLine(((String[])listbox.Items[1])[2]); // result => 0
Console.ReadLine();
}

ListBoxes use pointers, updating the value in the first array you are updating the value of the pointer labled "s", in order to use the same value name but different arrays you have to clone the starting array
ListBox listbox = new ListBox();
String[] s = new string[] { "5", "2", "3" };
listbox.Items.Add(s);
s = (String[])s.Clone();
s[2] = "0";
listbox.Items.Add(s);
Console.WriteLine(((String[])listbox.Items[0])[2]); // result => 3
Console.WriteLine(((String[])listbox.Items[1])[2]); // result => 0
Console.ReadLine();

With listbox.Items.Add(s); you are adding only one item being the array itself. Use AddRange instead to add the elements of the array.
listbox.Items.AddRange(s);
Another way to make it work is to set the DataSource:
listbox.DataSource = s;
Let's see what happens in your codeh in detail (with line numbers)
1 String[] s = new string[] { "5", "2", "3" };
2 listbox.Items.Add(s);
3 s[2] = "0";
4 listbox.Items.Add(s);
An array is created and initialized.
This array is added as one single item to the ListBox. Note that the array is a reference type. So indeed, you are only adding a reference to the ListBox, not a copy of the array.
One element of the array is changed. This affects the first item added to the ListBox as well, since it contains a reference to this unique array.
The same array-reference is added as an item to the ListBox. Now the ListBox contains 2 items referencing the same array with the same elements.
If you want the items to contain 2 distinct arrays, you can clone the array:
string[] s = new string[] { "5", "2", "3" };
listbox.Items.Add(s);
var s2 = (string[])s.Clone();
s2[2] = "0";
listbox.Items.Add(s2);
Now you have two distinct items in the ListBox. Note that the Array.Clone Method creates a shallow clone. I.e. the array elements themselves are not cloned. So, if they are reference types, both arrays will contain the same objects just after cloning. But since you have 2 distinct arrays, you can replace elements of an array without affecting the other array.
You can add a clone method to your own classes
public class MyOwnClass
{
public string Prop1 { get; set; }
public int Prop2 { get; set; }
public MyOwnClass ShallowClone()
{
return (MyOwnClass)MemberwiseClone();
}
}
MemberwiseClone is inherited from System.Object.

It is showing the last updated values because string are of reference types which will replace the all existing references on update. So you need create new array and then add to the list box as source.
static void Main(string[] args)
{
ListBox listbox = new ListBox();
String[] s = new string[] { "5", "2", "3" };
listbox.Items.Add(s);
String[] s2 = new string[] { "5", "2", "0" };
listbox.Items.Add(s2);
Console.WriteLine(((String[])listbox.Items[0])[2]); // result => 0
Console.WriteLine(((String[])listbox.Items[1])[2]); // result => 0
Console.ReadLine();
}

Related

C# Splitting a list based on items

I have a list with following items (lets call it 'main list'):
[1,2,2,3,1,2,3,4,4,1,2,2]
My goal is to split it into smaller lists that will contain '1' and the following numbers (until the next '1' occurs).
The end result should look like this:
[1,2,2,3]
[1,2,3,4,4]
[1,2,2]
Some additional info:
small lists can be of different lengths
the main list always starts with '1' (so you don't have to look for the beginning)
the elements of the main list and smaller lists are strings!
the number of smaller lists depends on the number of '1' in the main list
Basically I need to split a list of strings to smaller lists of strings.
The first idea that came to my mind was to create a ('final') list containing smaller lists, so I created a loop:
List<string> templist = new List<string>();
List<List<string> final = new List<List<string>();
foreach (string log in mainlist)
{
if (log != '1')
{
templist.Add(log);
}
else
{
final.add(templist);
templist.Clear();
templist.Add(log)}
}
final.add(templog);
it seems to work but I get a list with duplicates:
[[1,2,2],[1,2,2],[1,2,2]]
you can do this.
check for 1 and initialize the current list with default item 1 (add the current list to the final list in this step as well)
and if not one then keep on adding the items to the current list.
List<string> currentList = null;
List<string> mainList = new List<string> { "1", "2", "2", "3", "1", "2", "3", "4", "4", "1", "2", "2" };
List<List<string>> finalList = new List<List<string>>();
foreach (string item in mainList)
{
if (item == "1")
{
currentList = new List<string>() { item };
finalList.Add(currentList);
}
else
{
currentList.Add(item);
}
}
Can be:
else
{
templist = new List<string>(){log};
final.add(templist);
}
You can find the index of all occurrences of 1. Then according to these indexes and using the two functions Take() and Skip() to separate the lists..
List<string> mainlist = new List<string>{"1","2","2","3","1","2","3","4","4","1","2","2"};
List<List<string>> final = new List<List<string>>();
var numberOfList = mainlist.Select((item, index) => new {val = item,ind = index})
.Where(item => item.val == "1").ToList();
for(int i = 0; i<numberOfList.Count;i++)
{
if(i == numberOfList.Count-1)
final.Add(mainlist.Skip(numberOfList[i].ind)
.Take(mainlist.Count - numberOfList[i].ind).ToList());
else
final.Add(mainlist.Skip(numberOfList[i].ind)
.Take(numberOfList[i + 1].ind - numberOfList[i].ind).ToList());
}
result:
[1,2,2,3]
[1,2,3,4,4]
[1,2,2]

C# - Determine if all values of a single element in a list of objects are in another list

Currently I am working with something similar to the following:
if(listA.All(x => x.myHouseNumber == "1" || x.myValue == "2")
{
//do something
}
listA is a list of elements (myName, myHouseNumber, myStreet)
basically, I want the condition to be true if every myHouseNumber in listA is found in listB which is just a list of numbers
So if listA contains Bill,1,Main and Ted,2,Second
and listB contains 1,2,3 the condition is true because 1 and 2 are found within list B. if listB contained 1,5,9 the condition would be false because 2 is missing.
I am having trouble understanding how to compare a list with a single item to a single element in a list with several items.
The end result I am hoping would be something like this which I cannot seem to get to work
if(listA.All(x => x.myHouseNumber.Contains(listB.Any())))
{
//do something
}
Hopefully someone will understand what I am going for and be able to provide some assistance
Thanks in advance!
If I understand is correct, you should use the listB contains myHouseNumber.
the demo code.
public class test {
public string myName;
public string myHouseNumber;
public string myStreet;
}
var listA = new List<test>
{ new test { myHouseNumber = "1", myName = "Bill", myStreet = "Main" },
new test { myHouseNumber = "2", myName = "Ted", myStreet = "Second" }
};
var listB = new List<string> { "1", "2", "3" };
var listC = new List<string> { "1", "5", "9" };
// it is true
listA.All(p => listB.Contains(p.myHouseNumber))
// it is false
istA.All(p => listC.Contains(p.myHouseNumber))

Delete specific array without delete same value in another index or location [duplicate]

This question already has answers here:
How to remove the first element in an array? [duplicate]
(4 answers)
Closed 3 years ago.
I have a string[] with the same value but different index or location:
string[] test = {"jane", "joy", "adam", "jane"};
I want to delete the first jane without deleting the last jane:
string[] test = {"joy", "adam", "jane"};
I am using this method to delete:
string[] newTest = test.Where(w => w != test[0]).ToArray();
But that deletes all janes:
string[] newTest = {"joy", "adam"};
You can find index of element first and then remove it from list
something like,
string[] test = {"jane", "joy", "adam", "jane"};
List<string> list = new List<string>(test);
list.RemoveAt(list.FindIndex(x => x == "jane"));
Console.WriteLine(string.Join(",", list));
If you just want to delete first element from an array then there are multiple ways to achieve this,
First :
string[] test = {"jane", "joy", "adam", "jane"};
var result = test.Skip(1);
//If you want to convert it to array then
//string[] result = test.Skip(1).ToArray();
Console.WriteLine(string.Join(",", result));
Second : #Caius suggested
string[] test = {"jane", "joy", "adam", "jane"};
var result = test.RemoveAt(0);
Check index in Where clause,
string[] test = {"jane", "joy", "adam", "jane"};
var result = test.Where((x, i) => i != 0);
POC : .net Fiddle
in my project i using this method to delete
But that's saying "I want a new array where no element is the word "jane" so it removes all of them
Here's one method to remove an arbitrary index from an array using a List:
List<string> l = new List<string>(test);
l.RemoveAt(0);
string[] newTest = l.ToArray();
To be honest, you might be better to avoid using an array all together, and just use a List as your storage device if you need this kind of edit
Consider also the advice found here: Remove element of a regular array
If your question is "I want to remove the first occurrence of jane but not the second", let's find the first jane, then make a new array that excludes it:
List<string> l = new List<string>(test.Length);
bool removing = true;
foreach(string s in test) {
if(removing && s == "jane")
removing = false;
else
l.Add(s);
}
string[] newTest = l.ToArray();
There are other ways; you could indexOf to find the first jane, and then Array.Copy everything up to but not including the first instance, and everything after, not including etc.. This code is fairly self documenting though - iterate the array, looking for jane, if we find her and we're still in removing mode, just turn removing mode off (this jane is then never added it to the list, and turning off the removing flag means we'll certainly add the rest of the names into the list) and carry on processing the rest of the list
Consider how much simpler this code would be though if you were just using a List as your storage container:
test.RemoveAt(test.IndexOf("jane"));

How do I identify the selected ComboBox's Item relating it to a set of data?

Suppose I have a fixed set of identifiers organized in, for example, a dictionary.
public static Dictionary<int, string> clientIds = new Dictionary<int, string>()
{
{ 0, "aa" },
{ 1, "ab" },
{ 2, "ac" },
{ 3, "ba" },
{ 4, "bb" }
};
Then, I dynamically, at runtime, add to a ComboBox "friendly names" that are related to those identifiers (I don't know, however, which ones will be added.
clientIdCombo.Add(friendlyName);
Suppose that, at index zero, the friendly name "Alpha Beta" is added. That would relate to "ab" identifier. How can I know the user has selected the "ab" identifier, without having to condition based off the text displayed on the combo to the user? I tried using a BindingList instead, but that only provides me with that displayed text as well.
It sounds like something simple - how can I add underlying data to each ComboBox index? The simplest possible approach would be preferable, though multiple solutions are welcome.
Thanks.
ComboBox items may be of any type you like and the items' display name would be defined by this type's ToString method.
This means you may define your item type like this:
class ClientItem
{
public int Index;
public string Id;
public string FriendlyName;
public override string ToString()
{
return FriendlyName;
}
}
And fill your combo box with instances of this class.
comboBox.Items.Add(new ClientItem {
Index = 1,
Id = "ab",
FriendlyName = "Alpha Beta",
});
Then you can use the combo box items accessing all item's data. Just remember to cast to the specific item type:
var client = comboBox.SelectedItem as ClientItem;
MessageBox.Show(client.Id + ": " + client.FriendlyName);
Just add as many items to the ComboBox as there are in the array, each with its corresponding text and in the same order that they are on the array. Then, when you want to get the selected item from the array, get the selected index using ComboBox.SelectedIndex and get the item from the array corresponding to that index. In your case you are using a int-indexed dictionary, which behaves like an array in terms of indexing it.
TLDR:
string[] array = new[] { "aa", "ab", "ac" };
//This array is "equivalent" to
Dictionary<int, string> dic= new Dictionary<int, string>()
{
{ 0, "aa" },
{ 1, "ab" },
{ 2, "ac" },
};
//Add the items to your ComboBox, for simplicity let's use this
ComboBox.Items.Add("Alfa Alfa");
ComboBox.Items.Add("Alfa Bravo");
ComboBox.Items.Add("Alfa Charlie");
//Later, when retrieving the selected item
int selIndex = ComboBox.SelectedIndex;
string selItem = array[selIndex];

find if there is a common string between 2 list of strings using linq

I have two Lists of strings:
List<string> lotterynumber;
List<string> lotterywinningnumber;
I want to see if a lotterynumber is within the lotterywinningnumber.
I am using a foreach loop right now:
bool b = false;
foreach(string s in lotterynumber)
{
if(lotterywinningnumber.contains(s))
{
b= true;
}
}
Is there a way I can do it in Linq?
You can do this using Enumerable.Intersect. It will return a new Enumerable containing the items that exist in both collections.
var lotteryNumbers = new List<string>() { "1", "2", "3" };
var lotteryWinningNumbers = new List<string>() { "2", "3", "5" };
var numbersInBoth = lotteryNumbers.Intersect(lotteryWinningNumbers); // { "2", "3" }
From MSDN:
The intersection of two sets A and B is defined as the set that contains all the elements of A that also appear in B, but no other elements. When the object returned by this method is enumerated, Intersect enumerates first, collecting all distinct elements of that sequence. It then enumerates second, marking those elements that occur in both sequences. Finally, the marked elements are yielded in the order in which they were collected.
The benefit to using Intersect is that it will return the values that exist in both collections, instead of just a Boolean value.
Obviously, if a Boolean value is what you need, you just have to check if the resulting collection contains any elements:
bool contains = lotteryNumbers.Intersect(lotteryWinningNumbers).Any();
There is a way, but the efficiency will be the same: O(n).
Use Any
bool b = lotterynumber.Any(a => lotterywinningnumber.Contains(a));

Categories