Multidimensional Lists in C# - c#

At the moment I am using one list to store one part of my data, and it's working perfectly in this format:
Item
----------------
Joe Bloggs
George Forman
Peter Pan
Now, I would like to add another line to this list, for it to work like so:
NAME EMAIL
------------------------------------------------------
Joe Bloggs joe#bloggs.com
George Forman george#formangrills.co
Peter Pan me#neverland.com
I've tried using this code to create a list within a list, and this code is used in another method in a foreach loop:
// Where List is instantiated
List<List<string>> list2d = new List<List<string>>
...
// Where DataGrid instance is given the list
dg.DataSource = list2d;
dg.DataBind();
...
// In another method, where all people add their names and emails, then are added
// to the two-dimensional list
foreach (People p in ppl.results) {
list.Add(results.name);
list.Add(results.email);
list2d.Add(list);
}
When I run this, I get this result:
Capacity Count
----------------
16 16
16 16
16 16
... ...
Where am I going wrong here. How can I get the output I desire with the code I am using right now?

Why don't you use a List<People> instead of a List<List<string>> ?

Highly recommend something more like this:
public class Person {
public string Name {get; set;}
public string Email {get; set;}
}
var people = new List<Person>();
Easier to read, easy to code.

If for some reason you don't want to define a Person class and use List<Person> as advised, you can use a tuple, such as (C# 7):
var people = new List<(string Name, string Email)>
{
("Joe Bloggs", "joe#bloggs.com"),
("George Forman", "george#formangrills.co"),
("Peter Pan", "me#neverland.com")
};
var georgeEmail = people[1].Email;
The Name and Email member names are optional, you can omit them and access them using Item1 and Item2 respectively.
There are defined tuples for up to 8 members.
For earlier versions of C#, you can still use a List<Tuple<string, string>> (or preferably ValueTuple using this NuGet package), but you won't benefit from customized member names.

Where does the variable results come from?
This block:
foreach (People p in ppl.results) {
list.Add(results.name);
list.Add(results.email);
list2d.Add(list);
}
Should probably read more like:
foreach (People p in ppl.results) {
var list = new List<string>();
list.Add(p.name);
list.Add(p.email);
list2d.Add(list);
}

It's old but thought I'd add my two cents...
Not sure if it will work but try using a KeyValuePair:
List<KeyValuePair<?, ?>> LinkList = new List<KeyValuePair<?, ?>>();
LinkList.Add(new KeyValuePair<?, ?>(Object, Object));
You'll end up with something like this:
LinkList[0] = <Object, Object>
LinkList[1] = <Object, Object>
LinkList[2] = <Object, Object>
and so on...

You should use List<Person> or a HashSet<Person>.

Please show more of your code.
If that last piece of code declares and initializes the list variable outside the loop you're basically reusing the same list object, thus adding everything into one list.
Also show where .Capacity and .Count comes into play, how did you get those values?

Related

Add object to ICollection and then Add ICollection to a List<> ASP.NET

I'm not sure if this is possible or even makes total sense, but I need to add an object let's call it Person to an ICollection List People and add the collection to a List<> , in order to have the collection of people in a List<> that also will contain other parameters.
I am not sure how to do this but I can show you what I have sketched so far.
public void addPeopleToList(string PersonId)
{
Person p = findPerson(PersonId); /*Method that takes the ID and
returns an object from another List*/
ICollection<People> ICollectionPeople; //Create the ICollection
ICollectionPeople.Add(p); //Add Person to Collection
List.Add(ICollectionPeople); //Add Collection to List
}
If this way is not the proper way to do it, I am open to all other suggestions.
It's probably easier if you simply told us what exactly you want to achieve, but anyway:
You need to assign a value to ICollectionPeople (also maybe it's worth renaming it and following the naming conventions). Maybe
ICollection<People> peopleCollection = new List<People>();
Though do you really need that explicit type? You could use var.
You need to create an instance of the list you want to add your collection of people to. Maybe
var list = new List<People>();
Then finally, use AddRange() like
list.AddRange(peopleCollection);
I think what you need is another class.. like...
public class PersonWithAttributes : Person {
// add attribute properties here
}
Then in your code above you would change List to be of type List<PersonWithAttributes> and instead of .Adding the collection you would call List.AddRange(ICollectionPeople). After which you would need to loop over the List items and add the extra attributes you were talking about.
Not sure but looks like you wanted to have a List<List<People>> .. if that's so then your code is missing initialization of the collection. You need to change a bit
public void addPeopleToList(string PersonId)
{
Person p = findPerson(PersonId); /*Method that takes the ID and
returns an object from another List*/
List<Person> ICollectionPeople = new List<Person>(); //Create the
ICollectionPeople.Add(p); //Add Person to Collection
List<List<Person>> personLists = new List<List<Person>>()
personLists.Add(ICollectionPeople); //Add Collection to List
}

Why is the delimiter excluded in this String.Join? C#

I have an ObservableCollection<T> that I use for binding that I want to put into a String.Join statement, but I don't understand why it is giving the results I am getting and how to fix it?
This is the code I am using to get the result,
First I am getting the data I need via this LINQ query,
public static IEnumerable<string> GetNursingHomeNames(string home)
{
return DataContext.NursingHomeNameServerTables.Where(p => p.Nursing_Home_Section == home)
.Select(p => p.Nursing_Home_Name).Distinct();
}
I then put it into the ObservableCollection<T> (You may be wondering why I am not using an ObservableCollection<T> with the LINQ query, but I have my reasons.)
public static void NursingHomeNamesCollection(ObservableCollection<string> nursingHomeNames, string nursingHomeSection)
{
var homeNames = GetNursingHomeNames(nursingHomeSection);
if (homeNames == null)
{
return;
}
foreach (var item in homeNames)
{
nursingHomeNames.Add(item);
}
}
This is the property in the main window,
public ObservableCollection<string> NursingHomeNames { get; set; } =
new ObservableCollection<string>();
Then
Than I use Join to get the results for a specific purpose I need,
var result = String.Join(#",", NursingHomeNames.ToList());
And this gives the following result where there is no delimiter only a space,
foo bar bat baz
However, if just do this,
ObservableCollection<string> observableCol = new ObservableCollection<string>() { "foo", "bar", "bat", "baz" };
var result = String.Join(#",", observableCol.ToList());
The result is displayed with the delimiters in place.
foo,bar,bat,baz
Why is it doing this and is there a way to ensure the delimiters are correctly placed?
I know I have to work on my naming conventions.
EDIT: In the debuger, this is what I see,
When assigning the collection to a variable named data and viewing the results in the Watch Window
var data = NursingHomeNames.ToList();
Count = 4
[0] "foo"
[1] "bar"
[2] "bat"
[3] "baz"
However, I cannot reproduce this using any other code that does not use the LINQ query that pulls the data from the database. I tried making a new list and passing that list through the same code, but the error does not occur. I am sorry, but I can't post an example that can be reproduced.
As it turns out, after weeks of effort to figure this out, the answer to be had was with the comment that #Panagiotis Kanavos and #CodeCaster made.
I was using a Unicode character that looked like a comma and it was therefore creating a different behavior than what I was expecting.
In method
public static void NursingHomeNamesCollection(string nursingHomeSection)
you get string parameter to input. After in this method scope you add this string into static collection, but item in this scope is a char.
foreach (var item in homeNames)
You're trying to add a character at a time and join one big string. You need get collection to input of this method.

C# Dynamic Array/ index

I am creating an array of string[] in my c# program to save location ("name","Position") of a bunch of elements. The problem is any time I had to introduce a new element I have to change the code at several places according to index of elements:
string[] list = new string[4];
list[0] = "[ELEMENT #1 NAME],[ELEMENT #1POSITION]";
list[1] = "[ELEMENT #2 NAME],[ELEMENT #2POSITION]";
list[2] = "[ELEMENT #3 NAME],[ELEMENT #3POSITION]";
list[3] = "[ELEMENT #4 NAME],[ELEMENT #4POSITION]";
What I am looking for is something like an dynamic array so that I do not have to change the index location every time I introduce/ remove an element from list.
You can use List<string> as a dynamic array, it supports IEnumerable<string> for enumerating, or you can call LINQ and ToArray().
For example:
var list = new List<string>();
list.Add("[ELEMENT #1 NAME],[ELEMENT #1POSITION]");
string array[] = list.ToArray();
However, I'd actually recommend a dictionary in this case and not a list, a dictionary will let you store key-value pairs.
For example:
var dict = new Dictionary<string,int>();
dict["Element #1 Name"] = #Element #1 Position#;
Note that I've no real idea what type the position is, could be an int, a string or even a Point, but you get the idea.
You then don't need to bother with indices but refer to everything by name:
var el1_pos = dict["Element #1 Name"];
var el999_pos = dict["Element #999 Name"];
You can use List<T> if you want a dynamically sized collection and don't bother with the index. And you should also create a type with two properties (Name and Position) and have a list of that type instead of storing them as string. It's easier to maintain, you don't have to parse the string every time you wanna get/set the Name or Position of a particular object.
Normally, you would just use a List<String> here. The Add method allows you to just add an element, no indexing required.
List<string> list = new List<string>();
list.Add("Test");
In your case, since you have "Name" and "Position" associated with each other, consider using a List<PositionedThing> (a custom class in other words) or a Dictionary<String, String> to store your mappings.
The class would look like:
public class PositionedThing
{
public String Name {get; set;}
public String Position {get; set;}
}
Try
List<string> list = new List<string>();
list.Add("[ELEMENT #1 NAME],[ELEMENT #1POSITION]")
Unless I've misunderstood your question that should be what you want

Creation and recalling of objects in a foreach loop?

Beginner programmer here so please keep (explanation of) answers as simple as possible.
For an assignment we got a text file that contains a large amount of lines.
Each line is a different 'Order' with an ordernumber, location, frequency (amount of times per week), and a few other arguments.
Example:
18; New York; 3PWK; ***; ***; etc
I've made it so that each line is read and stored in a string with
string[] orders = System.IO.File.ReadAllLines(#<filepath here>);
And I've made a separate class called "Order" which has a get+set for all the properties like ordernumber, etc.
Now I'm stuck on how to actually get the values in there. I know how string splitting works but not how I can make unique objects in a loop.
I'd need something that created a new Order for every line and then assign Order.ordernumber, Order.location etc.
Any help would be MUCH appreciated!
An easy approach will be to make a class to define the orders like this:
public class Order{
public string OrderNumber{get;set;}
public string OrderId{get;set;}
public string OrderSomeThingElse{get;set;}
}
Then initialize a List:
var orderList = new List<Order>();
Then loop through and populate it:
foreach( var order in orders ){
var splitString = order.Split(';');
orderList.Add( new Order{
OrderNumber = splitString[0],
OrderId = splitString[1],
OrderSomeThingElse = splitString[2]
});
}
If you want an easy, but not that elegant approach, this is it.
In addition to all the good answers you've already received. I recommend you to use File.ReadLines() instead File.ReadAllLines() Because you are reading large file.
The ReadLines and ReadAllLines methods differ as follows: When you use ReadLines, you can start enumerating the collection of strings before the whole collection is returned; when you use ReadAllLines, you must wait for the whole array of strings be returned before you can access the array. Therefore, when you are working with very large files, ReadLines can be more efficient. MSDN
Unless I misunderstand... do you mean something like this?
var ordersCollection = new List<Order>();
foreach (var order in orders)
{
var o = new Order();
o.PropertyName = ""; // Assign your property values like this
ordersCollection.Add(o);
}
// ordersCollection is now full of your orders.

How do you concatenate Lists in C#?

If I have:
List<string> myList1;
List<string> myList2;
myList1 = getMeAList();
// Checked myList1, it contains 4 strings
myList2 = getMeAnotherList();
// Checked myList2, it contains 6 strings
myList1.Concat(myList2);
// Checked mylist1, it contains 4 strings... why?
I ran code similar to this in Visual Studio 2008 and set break points after each execution. After myList1 = getMeAList();, myList1 contains four strings, and I pressed the plus button to make sure they weren't all nulls.
After myList2 = getMeAnotherList();, myList2 contains six strings, and I checked to make sure they weren't null... After myList1.Concat(myList2); myList1 contained only four strings. Why is that?
Concat returns a new sequence without modifying the original list. Try myList1.AddRange(myList2).
Try this:
myList1 = myList1.Concat(myList2).ToList();
Concat returns an IEnumerable<T> that is the two lists put together, it doesn't modify either existing list. Also, since it returns an IEnumerable, if you want to assign it to a variable that is List<T>, you'll have to call ToList() on the IEnumerable<T> that is returned.
targetList = list1.Concat(list2).ToList();
It's working fine I think so. As previously said, Concat returns a new sequence and while converting the result to List, it does the job perfectly.
It also worth noting that Concat works in constant time and in constant memory.
For example, the following code
long boundary = 60000000;
for (long i = 0; i < boundary; i++)
{
list1.Add(i);
list2.Add(i);
}
var listConcat = list1.Concat(list2);
var list = listConcat.ToList();
list1.AddRange(list2);
gives the following timing/memory metrics:
After lists filled mem used: 1048730 KB
concat two enumerables: 00:00:00.0023309 mem used: 1048730 KB
convert concat to list: 00:00:03.7430633 mem used: 2097307 KB
list1.AddRange(list2) : 00:00:00.8439870 mem used: 2621595 KB
I know this is old but I came upon this post quickly thinking Concat would be my answer. Union worked great for me. Note, it returns only unique values but knowing that I was getting unique values anyway this solution worked for me.
namespace TestProject
{
public partial class Form1 :Form
{
public Form1()
{
InitializeComponent();
List<string> FirstList = new List<string>();
FirstList.Add("1234");
FirstList.Add("4567");
// In my code, I know I would not have this here but I put it in as a demonstration that it will not be in the secondList twice
FirstList.Add("Three");
List<string> secondList = GetList(FirstList);
foreach (string item in secondList)
Console.WriteLine(item);
}
private List<String> GetList(List<string> SortBy)
{
List<string> list = new List<string>();
list.Add("One");
list.Add("Two");
list.Add("Three");
list = list.Union(SortBy).ToList();
return list;
}
}
}
The output is:
One
Two
Three
1234
4567
Take a look at my implementation. It's safe from null lists.
IList<string> all= new List<string>();
if (letterForm.SecretaryPhone!=null)// first list may be null
all=all.Concat(letterForm.SecretaryPhone).ToList();
if (letterForm.EmployeePhone != null)// second list may be null
all= all.Concat(letterForm.EmployeePhone).ToList();
if (letterForm.DepartmentManagerName != null) // this is not list (its just string variable) so wrap it inside list then concat it
all = all.Concat(new []{letterForm.DepartmentManagerPhone}).ToList();

Categories

Resources