C# Trouble adding to a generic list - c#

This is a follow up question from another post but that post was answered. I have a for loop that I want to add three items to a generic class and I'm not sure how to do that. How do I add those items?
private static void TestResults()
{
List<Record> Records = new List<Record>();
for (int i = 0; i < ProxyList.Count; i++)
{
string[] split = List[i].Split('|');
// This is what i dont know how to do
// split[0] = Name, split[1] = SomeValue and split[3] = OrderNr
}
}
class Records
{
public static string Name { get; set; }
public static int SomeValue { get; set; }
public static int OrderNr { get; set; }
}

The first step is to associate the fields with instances of Records vs. the type itself. This is done by removing the static modifier
class Records
{
public string Name { get; set; }
public int SomeValue { get; set; }
public int OrderNr { get; set; }
}
To actually create instances try the following
for (int i = 0; i < ProxyList.Count; i++) {
string[] split = List[i].Split('|');
Records record = new Records() {
Name = split[0]
SomeValue = Int32.Parse(split[1])
OrderNr = Int32.Parse(split[2])
};
Records.add(record);
}
This particular example uses an object initializer to combine the acts of creating the object and settings its fields. This could be expanded into the longer form as follows
for (int i = 0; i < ProxyList.Count; i++) {
string[] split = List[i].Split('|');
Records record = new Records();
record.Name = split[0];
record.SomeValue = Int32.Parse(split[1]);
record.OrderNr = Int32.Parse(split[2]);
Records.add(record);
}
The Int32.Parse method will throw an exception if the item in the original string wasn't a number. If this is a possibility (think bad input) then you'll want to wrap the creation of Records with a try / catch statement

Well for one thing, your properties must not be static. You want different data for each instance, right? So they need to be instance properties. Personally I'd also make the type immutable, but that's another matter:
class Record // Changed from Records; each object is only a single record...
{
public string Name { get; set; }
public int SomeValue { get; set; }
public int OrderNumber { get; set; }
}
private static List<Record> ConvertRecords(IEnumerable<string> lines)
{
List<Record> records = new List<Record>();
foreach (string line in lines)
{
string[] split = line.Split('|');
Record record = new Record {
Name = split[0],
SomeValue = int.Parse(split[1]),
OrderNumber = int.Parse(split[2]);
};
records.Add(record);
}
}
As of C# 3 / .NET 3.5, a more idiomatic approach would be to use LINQ:
private static List<Record> ConvertRecords(IEnumerable<string> lines)
{
return (from line in lines
let split = line.Split('|')
select new Record {
Name = split[0],
SomeValue = int.Parse(split[1]),
OrderNumber = int.Parse(split[2]);
}).ToList();
}
}
... on the other hand, that's relatively advanced if you're really just starting to learn the language.
To be honest, Stack Overflow is better for asking specific questions than structured learning - I suggest you get hold of a good book, such as C# 4 in a Nutshell.

I'm not sure why you use static !!! but try this :
private static void TestResults()
{
List<Record> Records = new List<Record>();
for (int i = 0; i < ProxyList.Count; i++)
{;
string[] split = List[i].Split('|');
Records.Add(new Record() {Name = Namesplit[0] , SomeValue = split[1], OrderNr = split[3]}) ;
}
}

Related

CSV file has different rows

I have a csv file that looks like this:
M;2017.12.01 17:04;1;example#example.com
T;1;K001;2
T;1;N001;1
M;2017.11.01 15:56;2;example#example.com
T;2;P001;2
T;2;P001;1
My problem is that I have to read this file into a List<> and be able to navigate in it with indexes but the different types of rows after the long ones are confusing me.
class Order
{
public string Type { get; set; }
public DateTime Date { get; set; }
public string OrderID { get; set; }
public string Email { get; set; }
public string ItemNumber { get; set; }
public int Quantity { get; set; }
public Order(string[] ordered , string[] items)
{
Type = ordered[0];
Date = DateTime.Parse(ordered[1]);
OrderID = ordered[2];
Email = ordered[3];
Type = items[0];
OrderID = items[1];
ItemNumber = items[2];
Quantity = int.Parse(items[3]);
}
}
class Program
{
static List<Order> orders = new List<Order>();
static void Main(string[] args)
{
Reading();
}
private static void Reading()
{
using (System.IO.StreamReader reader = new System.IO.StreamReader("orders.csv"))
{
while (!reader.EndOfStream)
{
orders.Add(new Order(reader.ReadLine().Split(';') , reader.ReadLine().Split(';')));
}
}
}
}
You can try to identify the line before creating it.
Than you can create two different methods to initialize your order.
while (!reader.EndOfStream)
{
var values = reader.ReadLine().Split(';');
if(DateTime.TryParse(values.Skip(1).First(), out var date)) {
orders.Add(Order.FromOrderWithDate(values));
}
else
orders.Last().Items.Add(Item.FromOrderWithEmail(values));
}
The two methods will be something like
public static Order FromRow(string[] ordered) =>
new Order {
Type = ordered[0],
Date = DateTime.Parse(ordered[1]),
OrderID = ordered[2],
Email = ordered[3],
Items = new List<Item>();
};
public static Item FromRow(string[] items) =>
new Item {
Type = items[0],
OrderID = items[1],
ItemNumber = items[2],
Quantity = int.Parse(items[3])
};
And finally two different class, one for order and one for item, the Order should contain a list for the items.
Try something like:
List<Customer> customers = new List<Customer>();
Customer lastCustomer = null;
foreach(var line in File.ReadLines("orders.csv"))
{
var values = line.Split(';');
if (values[0]=="M")
{
lastCustomer = new Customer(values);
customes.Add(lastCustomer);
}
else if (values[0]=="T" && lastCustomer != null)
{
lastCustomer.AddOrder(values);
}
}
(you'll need to write a Customer class that can construct its self from an array of strings, plus has a method for adding new Order objects to its own list of orders, again constructing them from an array)

Add to List Property using the properties Index C#

I want to user a foreach loop to add to a c# list without using the list properties Key name.
I have a list such as
public class Bus
{
public string Val1 { get; set; }
public string Val2 { get; set; }
public string Val3 { get; set; }
public string Val4 { get; set; }
public string Val5 { get; set; }
public string Val6 { get; set; }
// ...
public string Val127 { get; set; }
}
The lists I want to populate can have over 200 properties so I am trying to find a quick way to populate them without writing out the properties.
I want to populate this from a one dimensional array (line) using something like this
j = 0
for (int i = 0; i < lines.Length; i++)
{
foreach(Bus BusProp in BusList)
{
BusProp[j] = line[i+j];
j =+ 1;
}
}
This is not working. Any suggestions are appreciated
If you can't change the class definition, your main alternative option is to use reflection.
void Main()
{
var bus = new Bus();
var data = new string[6] { "A", "B", "C", "D", "E", "F" };
for (var i = 1; i <= 6; i++)
{
bus.GetType().GetProperty("Val" + i.ToString()).SetValue(bus, data[i - 1]);
}
Console.WriteLine(bus.Val5); // E
}
public class Bus
{
public string Val1 {get;set;}
public string Val2 {get;set;}
public string Val3 {get;set;}
public string Val4 {get;set;}
public string Val5 {get;set;}
public string Val6 {get;set;}
}
Needless to say, this is quite expensive, and may be hard to maintain. Make sure you don't have a more reasonable option (e.g. changing the class to contain arrays instead of indexed properties, using code generation...) before using this.
Even if your database has some COBOL-like monstrosity with 150 indexed columns, there shouldn't be a reason why your application can't deal with them in the form of Item[34] instead of Item34 - isolate the application code from the fixed constraints you're not happy with.
Why not use
public class Bus
{
public string[] Val = new string[127];
}
j = 0;
for (int i = 0; i<lines.Length; i++)
{
foreach(Bus BusProp in BusList)
{
BusProp.Val[j] = line[i + j];
j =+ 1;
}
}
Try this
var typ = typeof(Bus);
var prop = typ.GetProperty($"Val{j}");
I feel like the answers so far haven't satisfied your needs, thus here's my solution:
static void Main(string[] args)
{
//Create a string array containing the desired property names, in this case I'll use a loop
List<string> DesiredProperties = new List<string>();
for (int i = 0; i < 100; i++)
{
DesiredProperties.Add(string.Format("Property{0}", i));
}
//Call the method that returns the object and pass the array as parameter
var Bus = CreateDynamicObject(DesiredProperties);
//Display one of the properties
Console.WriteLine(Bus.Property99);
Console.Read();
}
private static dynamic CreateDynamicObject(List<string> PropertyList)
{
dynamic obj = new System.Dynamic.ExpandoObject();
foreach (string Prop in PropertyList)
{
//You can add the properties using a dictionary. You can also give them an initial value
var dict = (IDictionary<string, object>)obj;
dict.Add(Prop, string.Format("The value of {0}", Prop));
}
return obj;
}
This piece of code will add 100 properties to var "Bus", which can be accessed and applied a value at will.

LINQ/Lambda update List items by spliting them into even groups

Some background of the application. This is for a WASP and SMPP transmitter accounts I have.
I have a list<> that contains an object that has all the necessary objects for a SMPP PDU and to send a message.
This list contains a property called "Routing Label" the routing label will indicate which one of the service providers to submit the PDU to (Vodacom, MTN, Cell C).
I have another list of accounts where I can bind to the SMPP server and send messages over. This list also contains the routing label and has a name for the transmitter account. So for example Vodacom's routing label is "D082" and I have two accounts that I can bind to simultaneously.
I now need to take the first List<> and update the fields. Lets say the first List<> has 1000 items. I will need to split these(more or less evenly) amongst all the accounts I have for each "Routing Label" that occurs in the second List<> mentioned.
I would prefer to achieve the desired functionality using linq or lambda.
Edited:Added code example so that you kind people may help me :-) Sorry for the poor standard but I quickly stripped some code out for you guys. I hope this helps with my problem.
static void Main(string[] args)
{
List<MobileAccounts> TransmitterAccounts = new List<MobileAccounts>();//This list contains my transmitter accounts
MobileAccounts item = new MobileAccounts();
item.FriendlyName = "Vodacom 1";
item.RoutingLabel = "D082";
TransmitterAccounts.Add(item);
MobileAccounts item1 = new MobileAccounts();
item1.FriendlyName = "Vodacom 2";
item1.RoutingLabel = "D082";
TransmitterAccounts.Add(item1);
MobileAccounts item2 = new MobileAccounts();
item2.FriendlyName = "MTN 1";
item2.RoutingLabel = "D083";
TransmitterAccounts.Add(item2);
MobileAccounts item3 = new MobileAccounts();
item3.FriendlyName = "MTN 2";
item3.RoutingLabel = "D083";
TransmitterAccounts.Add(item3);
MobileAccounts item4 = new MobileAccounts();
item4.FriendlyName = "MTN 3";
item4.RoutingLabel = "D083";
TransmitterAccounts.Add(item4);
MobileAccounts item5 = new MobileAccounts();
item5.FriendlyName = "CellC 1";
item5.RoutingLabel = "D084";
TransmitterAccounts.Add(item5);
MobileAccounts item6 = new MobileAccounts();
item6.FriendlyName = "CellC 2";
item6.RoutingLabel = "D084";
TransmitterAccounts.Add(item6);
List<SubmitSm> col = new List<SubmitSm>();//this list contains messages in a queue ready for sending
SubmitSm sitem = new SubmitSm();
sitem.DestAddr = "0722222222";//Vodacom number
sitem.RoutingLabel = "D082";
col.Add(sitem);
SubmitSm sitem1 = new SubmitSm();
sitem1.DestAddr = "0722222220";//Vodacom number
sitem1.RoutingLabel = "D082";
col.Add(sitem1);
SubmitSm sitem2 = new SubmitSm();
sitem2.DestAddr = "0722221212";//Vodacom number
sitem2.RoutingLabel = "D082";
col.Add(sitem2);
SubmitSm sitem3 = new SubmitSm();
sitem3.DestAddr = "0830000000";//MTN number
sitem3.RoutingLabel = "D083";
col.Add(sitem3);
SubmitSm sitem4 = new SubmitSm();
sitem4.DestAddr = "0833746005";//MTN number
sitem4.RoutingLabel = "D083";
col.Add(sitem4);
SubmitSm sitem5 = new SubmitSm();
sitem5.DestAddr = "0749999998";//CellC number
sitem5.RoutingLabel = "D084";
col.Add(sitem5);
/*
* Now this is where I will need
* to split all the messages in "col"
* amongst all the transmitter accounts
* I have.
*/
}
public class MobileAccounts
{
/*Please note not all items
are in this class. I have
* removed some as they are not
* neccessary for this demo code.
*/
//[DataMember]
public string FriendlyName;
//[DataMember]
public string BindName;
//[DataMember]
public string BindPassword;
//[DataMember]
public string BindHost;
//[DataMember]
public string BindPort;
//[DataMember]
public string BindType;
//[DataMember]
public string ProviderCode;
//[DataMember]
public string RoutingLabel;
}
public class SubmitSm
{
/*Please note not all items
are in this class. I have
* removed some as they are not
* neccessary for this demo code.
*/
public byte DefaultMessageId { get; set; }
public string DestAddr { get; set; }
public byte DestAddrNpi { get; set; }
public byte DestAddrTon { get; set; }
public string MessageText { get; set; }
public byte PriorityFlag { get; set; }
public byte ProtocolId { get; set; }
public byte RegisteredDelivery { get; set; }
public string ScheduleDeliveryTime { get; set; }
public string ServiceType { get; set; }
public string SourceAddr { get; set; }
public byte SourceAddrNpi { get; set; }
public byte SourceAddrTon { get; set; }
public string ValidityPeriod { get; set; }
public string RoutingLabel { get; set; }
}
Thank you to all who have contributed. #NinjaNye your solution is close but does not exactly fit my requirements. I do appreciate your effort though.
I think I am almost there but I am struggling. Can someone, anyone please help me figure out the sub select shown below:
List<IGrouping<string, MobileAccounts>> sad1 = TransmitterAccounts.GroupBy(y => y.RoutingLabel).ToList();
col = (List<SubmitSm>)col.Select
(x =>
{
x.ServiceType = sad1.Where
(z =>
z.Key== x.ServiceType
)
.Select
(y =>
new
{
//this should return the Transmitter account that has the lowest count
TransmitterAccount = y.OrderBy(ui => x.ServiceType.Count()).Select(ui => ui.FriendlyName).First()
}
).First().TransmitterAccount;
return x;
}
).ToList();
The only use of Linq I can see here would be the use of .Skip() and .Take() however I have created an extension method to tidy things up a bit. This will mean you can simply write the following to split any IEnumerable.
// In your example above you need to replace `items` with your `col` variable
var result = items.Split(transmitter.Count());
The extension method
http://www.ninjanye.co.uk/2013/07/splitting-distributing-list-objects.html
http://jnye.co/Posts/10/evenly-splitting-distributing-a-list-of-objects-using-linq-and-extension-methods
public static class EnumerableExtensions
{
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int groups)
{
var listedSource = source.ToList();
int extra;
int groupSize = Math.DivRem(listedSource.Count(), groups, out extra);
while (listedSource.Any())
{
int newSize = groupSize;
if (extra > 0)
{
newSize++;
extra--;
}
yield return listedSource.Take(newSize);
listedSource = listedSource.Skip(newSize).ToList();
}
}
}
The result
I set this up as a quick command program to test
Here are some results so you can see how items are split:
Initial post (now refactored above)
Something like this should do it... however I have simplified the example
// This is your transmitter count
int groups = 4;
// These are your SMS's
List<int> values = new List<int>(){1,2,3,4,5,6,7,8,9};
//Calculate group size
int extra;
int groupSize = Math.DivRem(values.Count, groups, out extra);
var result = new List<IEnumerable<int>>();
while (values.Any())
{
int newSize = groupSize;
if (extra > 0)
{
// Account for extras
newSize++;
extra--;
}
result.Add(values.Take(newSize));
values = values.Skip(newSize).ToList();
}
return result;
you shoud avoid LINQ with side effects. But you can attach the object in the lists like that.
List<MobileAccounts> mas = new List<MobileAccounts>();
List<SubmitSm> sms = new List<SubmitSm>();
var result = mas.Select(ma => new {Sm=(sms.First(sm => sm.RoutingLabel == ma.RoutingLabel)),Ma=ma});
foreach (var r in result)
{
//update Properties here
}

Combining numbers and names collections

I have 2 List collections. One contains numbers, the other names. There are twice as many numbers as names(always). I want to take the first name from the collection and the first two numbers from the other collection then put them together in a 3rd user collection of (VentriloUser). Then the second name needs to be matched with the 3rd and 4th numbers and so on.
I was thinking something with a for or foreach loop, but I can't wrap my head around it right now.
public class VentriloUser
{
public VentriloUser(string name, int seconds, int ping)
{
Name = name; Seconds = seconds; Ping = ping;
}
public string Name { get; set; }
public int Ping { get; set; }
public int Seconds { get; set; }
}
public class Ventrilo
{
public Ventrilo(string statusurl)
{
StatusURL = statusurl;
}
public string StatusURL { get; set; }
public string HTML { get; set; }
public List<VentriloUser> Users { get; set; }
private Regex findNumbers = new Regex("\\<td width=\"10%\" bgcolor=\"#\\w{6}\"\\>\\<font color=\"#000000\">\\<div style=\"overflow:hidden;text-overflow:ellipsis\"\\>-?\\d+\\<");
private Regex findNames = new Regex("\\<td width=\"20%\" bgcolor=\"#\\w{6}\"\\>\\<font color=\"#000000\">\\<div style=\"overflow:hidden;text-overflow:ellipsis\"\\>\\b\\w+\\<");
private WebClient wClient = new WebClient();
public void DownloadHTML()
{
HTML = wClient.DownloadString(StatusURL);
}
public List<int> GetNumbers()
{
var rawnumbers = findNumbers.Matches(HTML);
var numbers = new List<int>();
foreach (var rawnumber in rawnumbers)
{
var match = Regex.Match(rawnumber.ToString(), "\\>\\-?\\d+\\<");
string number = Regex.Replace(match.ToString(), "\\<|\\>", "");
numbers.Add(Convert.ToInt32(number));
}
return numbers;
}
public List<string> GetNames()
{
var rawnames = findNames.Matches(HTML);
var names = new List<string>();
foreach (var rawname in rawnames)
{
var match = Regex.Match(rawname.ToString(), "\\>\\w+<");
string name = Regex.Replace(match.ToString(), "\\<|\\>", "");
names.Add(name);
}
return names;
}
public List<VentriloUser> GenerateUsers()
{
var numbers = GetNumbers();
var names = GetNames();
var users = new List<VentriloUser>();
}
}
I am a hobbyist, but hope to pursue a career one day. Any help is much appreciated. Thank you for your time.
Using LINQ:
var users = names.Select((name,idx) => new VentriloUser(name, numbers[idx*2], numbers[idx*2+1]))
.ToList();
Using loops:
var users = new List<VentriloUser>();
for (int i = 0; i < names.Count; i++)
{
var name = names[i];
int j = i * 2;
if (j >= numbers.Count - 1)
break; // to be safe...
users.Add(new VentriloUser(name, numbers[j], numbers[j + 1]));
}

Convert arraylist to a list of dictionaries?

Does somebody know how to convert an arraylist to a list of dictionaries?
What do I have? I have an ArrayList (list) with a lot of strings:
foreach (string s in list)
{
Console.WriteLine(s);
}
output:
klaus
male
spain
lissy
female
england
peter
male
usa
...
As we see there is an order. The first entry is a NAME, second GENDER, third COUNTRY and then again NAME, GENDER... and so on.
Now for clarity I would like to store these attributes in a List of Dictionaries. Every List entry should be 1 Dictionary with these 3 Attributes. Is this a good idea? Whats the easiest way? I just search something to store this list in a better looking collection that is later easiert to handle. I have this:
List<Dictionary<string, string>> dlist = new List<Dictionary<string, string>>();
const int separate = 3;
foreach (string s in list)
{
//add list entries to dlist?
}
No it's not a good idea. Define a class.
At least something like this:
class Person
{
public string Name { get; set; }
public string Gender { get; set; }
public string Country { get; set; }
}
Even better would be to use an enum for Gender, and possibly a class (built-in or custom) for the country.
Anyway, to populate a collection with the above class, you'd use something like:
List<Person> result = new List<Person>();
for (int i = 0; i < list.Count; i += 3) {
result.Add(
new Person { Name = list[i], Gender = list[i+1], Country = list[i+2] });
}
Note that this loop lacks error checking on the count of items in the list, which should be a multiple of three.
enum Gender
{
Male = 0,
Female = 1
}
class Person
{
public string Name { get; set; }
public Gender Gender { get; set; }
public string Country { get; set; }
public Person(string name, Gender gender, string country)
{
this.Name = name;
this.Gender = gender;
this.Country = country;
}
}
Usage:
List<Person> persons = new List<Person>();
Person person;
for (int i = 0; i < list.Count; i += 3) {
{
person = new Person(list[i], (Gender)Enum.Parse(typeof(Gender), list[i+1], true), list[i+2]);
persons.add(person);
}
I would recommend using a class to encapsulate your entry:
class Entry
{
public String Name { get; set; }
public String Gender { get; set; }
public String Country { get; set; }
}
Then add to a list of Entrys
List<Entry> elist = new List<Entry>();
for (int i = 0; i < list.Count; i += 3)
{
var ent = new Entry() { Name = list[i],
Gender = list[i+1],
Country = list[i+2]};
elist.Add(ent);
}
Or you could use Tuples:
List<Tuple<string, string, string>> tlist =
new List<Tuple<string, string, string>>();
for (int i = 0; i < list.Count; i += 3)
{
Tuple<string,string, string> ent
= new Tuple<string,string,string>(list[i], list[i+1], list[i+2]);
tlist.Add(ent);
}
the simplest way to implement that - it's create a new class for representing someone in you list with 3 props:
public class Someone {
public string Name { get; set; }
public string Gender { get; set; }
public string Country { get; set; }
}
And than you can fill your destination list with Someone entries:
List<Someone> dList = new List<Someone>();
for(int i = 0; i < list.Count; i += 3)
dList.Add(new Someone() { Name = list[i], Gender = list[i+1], Country = list[i+2] });
p.s. this code may contain errors because I don't have access to computer with VS installed.
You may have a class to hold the data like:
public class Info
{
public string Name { get; set; }
public string Gender { get; set; }
public string Country { get; set; }
}
then you can have a dictionary like this
Dictionary<int, Info> infoDict = new Dictionary<int, Info>();
then you can start reading and prepare the dictionary.
var listSrc = new List<string>
{
"klaus",
"male",
"spain",
"lissy",
"england",
"female",
"peter",
"usa",
"male",
};
var dlist = new List<Dictionary<string, string>>();
for (var i = 0; i < listSrc.Count; i++)
{
var captions = new List<string>
{
"Name",
"Gender",
"Country"
};
var list = listSrc.Take(3).ToList();
listSrc.RemoveRange(0, 3);
dlist.Add(list.ToDictionary(x => captions[list.IndexOf(x)], x => x));
}
Its simple. You are working for a class that contains three data variables named :
String name;
String gender;
String country;
So make a class that contains all three of these variables. As for eg:
class Dictnary
{
public Dictnary(String nameFromCall, String genderFromCall, String countryFromCall)
{
this.name = nameFromCall;
this.gender = genderFromCall;
this.country = countryFromCall;
}
}
To answer the question in the title, this is probably the most succinct way to do the conversion:
for (int i = 0; i < list.Count; i += 3) {
dlist.Add(new Dictionary<string, string>() {
{ "name", (string)list[i] },
{ "gender", (string)list[i+1] },
{ "country", (string)list[i+2] }
});
}
(If you switch from an ArrayList to a List<string>, you can drop the (string) cast.)
That said, unless you really need a dictionary for some technical reason, your problem is better solved using one of the class-based approaches suggested in the other answers.

Categories