I'm really new to programming, so take this with a grain of salt.
I've made 2 arrays that correspond to eachother; One is a Name array and one is a Phone Number array. The idea is that the spot [1] in NameArray corresponds to spot [1] in the PhoneArray. In other words, I need to keep these 'pairings' in tact.
I'm trying to make a function that deletes one of the spots in the array, and shifts everything down one, as to fill the space left empty by the deleted element.
namearray = namearray.Where(f => f != iNum).ToArray();
is what I've tried, with iNum being the number corresponding to the element marked for deletion in the array.
I've also tried converting it to a list, removing the item, then array-ing it again.
var namelist = namearray.ToList();
var phonelist = phonearray.ToList();
namelist.Remove(txtName.Text);
phonelist.Remove(txtPhone.Text);
namearray = namelist.ToArray();
phonearray = phonelist.ToArray();
lbName.Items.Clear();
lbPhone.Items.Clear();
lbName.Items.AddRange(namearray);
lbPhone.Items.AddRange(phonearray);
with txtName.Text and txtPhone.Text being the strings for deletion in the corresponding list boxes.
Can someone suggest a better way to do it / What I'm doing wrong / How to fix?
Thanks guys
-Zack
A better way would be to have an array of a class that contains a Name and Phone Number object:
public class PersonData
{
public string Name { get; set; }
public string Phone { get; set; }
}
public PersonData[] data;
That way, instead of keeping two arrays in sync, it's one array with all the appropriate data.
Try a loop through both arrays, moving the values of each down an index each time.
Start the loop at the index of the value you want to delete. So you would find the IndexOf(T) the value you want, storing it as deleteIndex and run the loop starting from that index.
When you hit the end of the array, set the last value as null or string.Empty (depending what value type the array holds).
A bit like this:
var deleteIndex = namearray.IndexOf("TheStringYouWantToDelete");
for (int i = deleteIndex; i < namearray.Length; i++)
{
if (i == namearray.Length - 1) // The "last" item in the array.
{
namearray[i] = string.Empty; // Or null, or your chosen "empty" value.
phonearray[i] = string.Empty; // Or null, or your chosen "empty" value.
}
else
{
namearray[i] = namearray[i+1];
phonearray[i] = phonearray[i+1];
}
}
This will work for deleting and moving values 'down' in index.
You could also rewrite the code for moving them the other way, as it would work similarly.
Reordering them completely? Different ball game...
Hope this helps.
If the namearray and phonearray contain strings and you know the index of the element to remove (iNum) then you need to use the overload of the Where extension that takes a second parameter, the index of the current element in the evaluation
namearray = namearray.Where((x, y) => y != iNum).ToArray();
However the suggestion to use classes for your task is the correct one. Namearray and Phonearray (and whatever else you need to handle in future) are to be thought as properties of a Person class and instead of using arrays use a List<Person>
public class Person
{
public string FirstName {get; set;}
public string LastName {get; set;}
public string Phone {get; set;}
}
List<Person> people = new List<Person>()
{
{new Person() {FirstName="Steve", LastName="OHara", Phone="123456"}},
{new Person() {FirstName="Mark", LastName="Noname", Phone="789012"}}
};
In this scenarion removing an item knowing the LastName could be written as
people = people.Where(x => x.LastName != "OHara").ToList();
(or as before using the index in the list of the element to remove)
people = people.Where((x, y) => y != iNum).ToArray();
The other answers provide some better design suggestions, but if you're using ListBoxes and want to stick with arrays, you can do this to synchronize them:
int idx = lbName.Items.IndexOf(txtName.Text);
if (idx > -1)
{
lbName.Items.RemoveAt(idx);
lbPhone.Items.RemoveAt(idx);
}
namearray = lbName.Items.Cast<string>().ToArray<string>();
phonearray = lbPhone.Items.Cast<string>().ToArray<string>();
Use a dictionary instead.
Dictionary<string, string> phoneBook = new Dictionary<string, string>();
phoneBook["Foo"] = "1234567890";
phoneBook["Bar"] = "0987654321";
phoneBook.Remove("Bar");
Related
I have a list that is constantly being updated throughout my program. I would like to be able to compare the initial count and final count of my list after every update. The following is just a sample code (the original code is too lengthy) but it sufficiently captures the problem.
class Bot
{
public int ID { get; set; }
}
public class Program
{
public void Main()
{
List<Bot> InitialList = new List<Bot>();
List<Bot> FinalList = new List<Bot>();
for (int i = 0; i < 12345; i++)
{
Bot b = new Bot() {ID = i};
InitialList.Add(b);
}
FinalList = InitialList;
for (int i = 0; i < 12345; i++)
{
Bot b = new Bot() {ID = i};
FinalList.Add(b);
}
Console.Write($"Initial list has {InitialList.Count} bots");
Console.Write($"Final list has {FinalList.Count} bots");
}
}
Output:
Initial list has 24690 bots
Final list has 24690 bots
Expected for both lists to have 12345 bots.
What is correct way to copy the initial list so new set is not simply added to original?
To do what you seem to want to do, you want to copy the list rather than assign a new reference to the same list. So instead of
FinalList = InitialList;
Use
FinalList.AddRange(InitialList);
Basically what you had was two variables both referring to the same list. This way you have two different lists, one with the initial values and one with new values.
That said, you could also just store the count if that's all you want to do.
int initialCount = InitialList.Count;
FinalList = InitialList;
Although there's now no longer a reason to copy from one to the other if you already have the data you need.
I get the feeling you actually want to do more than what's stated in the question though, so the correct approach may change depending on what you actually want to do.
I want to sort a List Array on the basis of an array item.
I have a List Array of Strings as below:
List<String>[] MyProjects = new List<String>[20];
Through a loop, I have added five strings
(Id, Name, StartDate, EndDate, Status)
to each of the 20 projects from another detailed List source.
for(int i = 0; i<20; i++){
MyProjects[i].Add(DetailedProjectList.Id.ToString());
MyProjects[i].Add(DetailedProjectList.Name);
MyProjects[i].Add(DetailedProjectList.StartDate);
MyProjects[i].Add(DetailedProjectList.EndDate);
MyProjects[i].Add(DetailedProjectList.Status)}
The Status values are
"Slow", "Normal", "Fast", "Suspended" and "" for unknown status.
Based on Status, I want to sort MyProject List Array.
What I have done is that I have created another List as below
List<string> sortProjectsBy = new List<string>(){"Slow", "Normal", "Fast", "", "Suspended"};
I tried as below to sort, however unsuccessful.
MyProjects = MyProjects.OrderBy(x => sortProjectsBy.IndexOf(4));
Can anyone hint in the right direction. Thanks.
I suggest you to create class Project and then add all the fields inside it you need. It's much nicer and scalable in the future. Then create a List or an Array of projects and use the OrderBy() function to sort based on the field you want.
List<Project> projects = new List<>();
// Fill the list...
projects.OrderBy(project => project.Status);
The field Status has to be a primitive type or needs to implement the interface IComparable in order for the sorting to work. I suggest you add an enum for Status with int values.
First consider maybe to use Enum for status and put it in a different file lite (utils or something) - better to work like that.
enum Status {"Slow"=1, "Normal", "Fast", "", "Suspend"}
Now about the filtering you want to achieve do it like this (you need to tell which attribute of x you are referring to. In this case is status)
MyProjects = MyProjects.OrderBy(x => x.status == enum.Suspend);
Read about enums :
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/enum
Read about lambda expressions :
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions
First of all, storing project details as List is not adivisable. You need to create a Custom Class to represent them.
For example,
public class DetailedProjectList
{
public string Name {get;set;}
public eStatus Status {get;set;}
// rest of properties
}
Then You can use
var result = MyProjects.OrderBy(x=> sortProjectsBy.IndexOf(x.Status));
For example
List<string> sortProjectsBy = new List<string>(){"Slow", "Normal", "Fast", "", "Suspended"};
var MyProjects= new List<DetailedProjectList>{
new DetailedProjectList{Name="abc1", Status="Fast"},
new DetailedProjectList{Name="abc2", Status="Normal"},
new DetailedProjectList{Name="abc3", Status="Slow"},
};
var result = MyProjects.OrderBy(x=> sortProjectsBy.IndexOf(x.Status));
Output
abc3 Slow
abc2 Normal
abc1 Fast
A better approach thought would be to use Enum to represent Status.
public enum eStatus
{
Slow,
Normal,
Fast,
Unknown,
Suspended
}
Then your code can be simplified as
var MyProjects= new List<DetailedProjectList>{
new DetailedProjectList{Name="abc1", Status=eStatus.Fast},
new DetailedProjectList{Name="abc2", Status=eStatus.Normal},
new DetailedProjectList{Name="abc3", Status=eStatus.Slow},
};
var result = MyProjects.OrderBy(x=> x.Status);
Ok so you have a collection of 20 items. Based on them you need to create a list of strings(20 DetailedProjectList items).
What you can do to solve your problem is to SORT YOUR COLLECTION before you create your list of strings. In this way your list of strings will be sorted.
But your code is not optimal at all. So you should concider optimization on many levels.
Lets say you have ProjectDetail class as follow:
private class ProjectDetail
{
public int Id {get;set;}
public string Name {get;set;}
DateTime StartDate {get;set;} = DateTime.Now;
DateTime EndDate {get;set;} = DateTime.Now;
public string Status {get;set;}
public string toString => $"{Id} - {Name} - {StartDate} - {EndDate} - {Status}";
}
Notice that I have added a toString attribute to make things easier, and I also have added default values.
Then your program could be like:
static void Main(string[] args)
{
var projectDetails = MockProjectItems();
Console.WriteLine("Before sortig:");
foreach (var item in projectDetails)
{
Console.WriteLine(item.toString);
}
var myProjects = projectDetails.OrderBy(p => p.Status).Select(p => p.toString);
Console.WriteLine("\n\nAfter sorting:");
foreach (var item in myProjects)
{
Console.WriteLine(item);
}
}
where the helper method is
private static List<ProjectDetail> MockProjectItems()
{
var items = new List<ProjectDetail>(20);
for(int i = 0; i < 20 ; i += 4){
items.Add(new ProjectDetail{Id = i, Name = "RandomName "+i, Status = "Slow"});
items.Add(new ProjectDetail{Id = i+1, Name = "RandomName "+(i+1), Status = "Normal"});
items.Add(new ProjectDetail{Id = i+2, Name = "RandomName "+(i+2), Status = "Fast"});
items.Add(new ProjectDetail{Id = i+3, Name = "RandomName "+(i+3), Status = "Suspended"});
}
return items;
}
Then your program should print the following:
I need to insert a string (from one window(QueryBuilder)) into an array(of another window(Main)).
In the Main i have a method as
public void DisplayCalcQuery(string argFromQueryBuilder)
{
int itemsInUserBuiltQueries = UserBuiltQueries.Count();
UserBuiltQueries[itemsInUserBuiltQueries] = argFromQueryBuilder.ToString();
//displayng the user built query(queries) on the stack panel meant to display it.
foreach (string query in UserBuiltQueries)
{
CheckBox checkQueries = new CheckBox() { Content = query };
stackPanel1.Children.Add(checkQueries);
checkboxes.Add(checkQueries);
}
}
Where UserBuiltQueries is declared as
string[] UserBuiltQueries;
However when from the other window i do
backscreen.DisplayCalcQuery(ttextBox1.Text.ToString()); //where backscreen is the Main
The argument is passed well but i get an error as
{"Value cannot be null.\r\nParameter name: source"}
What did I do wrong ?
These lines are wrong
int itemsInUserBuiltQueries = UserBuiltQueries.Count();
UserBuiltQueries[itemsInUserBuiltQueries] = argFromQueryBuilder.ToString();
Arrays start at index zero and end at index (Count - 1), so, if UserBuiltQueries.Count() returns 10 you could use indexes from 0 to 9. Essentially, using index 10, you are adding a new string outside the end of the array.
However, if your requirements force you to expand the array, it is better and more easy to code if you use a List<string> instead. Adding new elements will be a lot more easier and you could still use the List as an Array for common tasks.
List<string> UserBuiltQueries = new List<string>();
.....
public void DisplayCalcQuery(string argFromQueryBuilder)
{
UserBuiltQueries.Add(argFromQueryBuilder);
//displayng the user built query(queries) on the stack panel meant to display it.
foreach (string query in UserBuiltQueries)
{
CheckBox checkQueries = new CheckBox() { Content = query };
stackPanel1.Children.Add(checkQueries);
checkboxes.Add(checkQueries);
}
}
By the way, you should stop to unnecessarily convert a string to a string. You pass a ttextBox1.Text.ToString() but ttextBox1.Text is already a string. Inside the method the parameter argFromQueryBuilder is already a string and there is no need to convert to a string
Instead of using string[] for UserBuildQueries, use List. When you need it as an array, you can simply say: UserBuildQueries.ToArry()
Rewrite the function to
public void DisplayCalcQuery(string argFromQueryBuilder)
{
UserBuiltQueries.Add(argFromQueryBuilder.ToString());
//displayng the user built query(queries) on the stack panel meant to display it.
foreach (string query in UserBuiltQueries)
{
CheckBox checkQueries = new CheckBox() { Content = query };
stackPanel1.Children.Add(checkQueries);
checkboxes.Add(checkQueries);
}
}
In c# but I think in all programming language indexis start from 0:
so if an array has length or count =1 the index is 0 array[0], array.lenght==1
int itemsInUserBuiltQueries = UserBuiltQueries.Count()-1;
UserBuiltQueries[itemsInUserBuiltQueries] = argFromQueryBuilder.ToString();
And double check that your array is initialized before using it!
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;
Is there a convenient way to remove a nested list from another list if it meets certain requirements? For example, say we have a collection of stops, and we decide to call each collection of stops a route. Each route is in list from. Then we decide to put each route into a list as well.
So now that we have a list of routes, someone decides that certain types of routes really shouldn't be included in the route list. How can I remove those routes? Here's some sample code:
Example Class
public class Stops
{
public Stops(int _param1, string _param2)
{
param1 = _param1;
param2 = _param2;
}
public int param1 { get; set; }
public string param2 { get; set; }
}
Create the Lists
List<List<Stops>> lstRoutes = new List<List<Stops>>();
List<Stops> lstStops = new List<Stops>();
List<Stops> lstMoreStops = new List<Stops>();
// Create some stops
for (int i = 0; i < 5; i++)
{
lstStops.Add(new Stops(i, "some text"));
}
lstRoutes.Add(lstStops);
// Create some more stops
for (int i = 5; i < 10; i++)
{
lstMoreStops.Add(new Stops(i, "some more text"));
}
lstRoutes.Add(lstMoreStops);
How can I remove any route from lstRoutes that has, say, any param1 value greater than 6?
The simplest way (which can be applicable to all enumerables, not just lists) would be:
lstRoutes = lstRoutes.Where(r => !r.Any(s => s.param1 > 6)).ToList();
The snippet above creates a new list, so copying will occur which means both the performance and memory usage will slightly suffer. The most efficient way would be not adding those items to the list in the first place.
The second most efficient way would be to remove items from the list instead of constructing a new one, so the memory usage wouldn't be affected as much:
lstRoutes.RemoveAll(r => r.Any(s => s.param1 > 6));
List<Stops> stop = lstRoutes.Find(delegate(List<Stops> stp) { return stp.param1 > 6; });