I have two classes:
public class ShopClass
{
public string shop_id { get; set; }
public List<CurrencyAmount> amount { get; set; }
}
public class CurrencyAmount
{
public string currency { get; set; }
public int? amount { get; set; }
}
I can add values to the class ShopClass like this:
ShopClass shop = new ShopClass();
shop.shop_id = "100";
List<CurrencyAmount> ca = new List<CurrencyAmount>();
ca.Add(new CurrencyAmount() { currency = "USD", amount = 1000});
shop.amount = ca;
I can get the value of shop_id like this:
Console.WriteLine(shop.shop_id);
But how can I get the value of the amount?
Many thanks for any suggestions.
You can do something like:
foreach (var v in shop.amount)
{
Console.WriteLine(v.currency + " " + v.amount);
}
Or alternatively, you can avoid string concatenation (thanks PC Luddite) and use:
foreach (var v in shop.amount)
{
Console.WriteLine("{0} {1}", v.currency, v.amount);
}
This snippet will print out all of the values in shop.amount. Since shop.amount refers to a list, you can't get the values by using Console.WriteLine(shop.amount), as this will just print out a reference to the list. Instead, you need to iterate through all of the items in shop.amount in order to get their values.
There is no "the" amount, you have a list of amounts. So you need to iterate or project the items in the amount property, for example using a foreach loop:
foreach (var amount in shop.amount)
{
Console.WriteLine("{0} {1}", amount.currency, amount.amount);
}
Your example can easily be reduced to a dictionary for direct lookup. I'm assuming you're looking to find the value for a specific currency and not print all values.
Store currencies and their values.
using System;
using System.Collections.Generic;
public class ShopClass
{
public ShopClass()
{
Amounts = new Dictionary<string, int>();
}
public string ShopID { get; set; }
public Dictionary<string, int> Amounts { get; private set; }
public void AddAmount(string currency, int amount)
{
if (Amounts.ContainsKey(currency))
{
Amounts[currency] = amount;
}
else
{
Amounts.Add(currency, amount);
}
}
public int? GetAmount(string currency)
{
if (Amounts.ContainsKey(currency))
{
return Amounts[currency];
}
return null;
}
public void PrintAmounts()
{
foreach (var currency in Amounts.Keys)
{
Console.WriteLine("{0} - {1}: {2}", ShopID, currency, GetAmount(currency));
}
}
}
Print currency values.
ShopClass shop = new ShopClass();
shop.ShopID = "100";
shop.AddCurrency("USD", 1000);
shop.PrintAmounts();
Related
I have s Student class where each student record has a list of Results.
I need to export there results to CSV and I'm using CsvHelper.
public class Student
{
public string Id { get; set; }
public string Name { get; set; }
public Result[] Grades { get; set; }
}
public class Result
{
public string Subject { get; set; }
public decimal? Marks { get; set; }
}
I'm using Reference Maps to map the list of Results, but when exporting to CSV it throws and error.
Mapping Code
public sealed class StudentResultExportMap : ClassMap<Student>
{
public StudentResultExportMap ()
{
AutoMap();
References<GradesMap>(m => m.Grades);
}
}
public sealed class GradesMap: ClassMap<Result>
{
public GradesMap()
{
Map(m => m.Subject);
Map(m => m.Marks);
}
}
Error
Property 'System.String Subject' is not defined for type
'{namespace}.GetStudentResults+Result[]' Parameter name: property
Unfortunately References<GradesMap>(m => m.Grades); doesn't work for an array of Result. It would work for an individual result. I have one solution, which overrides the ToString() method of Result to flatten the grades. It might work for you, depending on what you need.
public class Result
{
public string Subject { get; set; }
public decimal? Marks { get; set; }
public override string ToString()
{
return $"{Subject} = {Marks}";
}
}
Make a slight change to your StudentResultExportMap. You can set the 2nd number on .Index(2, 7) to handle the max number of grades you think a student might have.
public sealed class StudentResultExportMap : ClassMap<Student>
{
public StudentResultExportMap()
{
AutoMap();
Map(m => m.Grades).Name("Grade").Index(2, 7);
}
}
You will then get Id, Name, Grade1, Grade2, Grade3, Grade4, Grade5, Grade6 with the toString() value of Result for each grade.
var records = new List<Student>
{
new Student{ Id = "1", Name = "First", Grades = new [] {
new Result { Subject = "Subject1", Marks = (decimal)2.5 } ,
new Result { Subject = "Subject2", Marks = (decimal)3.5 } }},
new Student{ Id = "2", Name = "Second", Grades = new [] {
new Result { Subject = "Subject1", Marks = (decimal)3.5 } ,
new Result { Subject = "Subject2", Marks = (decimal)4.0 } }}
};
using (var writer = new StreamWriter("path\\to\\StudentResults.csv"))
using (var csv = new CsvWriter(writer))
{
csv.Configuration.RegisterClassMap<StudentResultExportMap>();
csv.WriteRecords(records);
}
I'm currently working on an application in C#. Imagine a store front with a checkout. I have a dictionary structure with an object as Key, and an int object counter as value.
the structure looks like this:
Dictionary<myObject, int> items.
The basic idea is, to pass a dictionary of Items into a method. I'm only adding unique myObjects to the dictionary. The myObject has a counter rule attached. Once the counter rule is full filled I want to do a calculation with all myObects in the dictionary.
The myObject looks like this:
public class myObject
{
string ItemId { get; set; }
Discount Discount { get; set; }
}
public class Discount
{
public int Count { get; set; }
public decimal Price { get; set; }
public IDiscountHandler DiscountHandler => new DiscountHandler();
}
A sample myObject could look like this:
var myObectA = new myObject()
{
ItemId = "A"
};
var discountA = new Discount()
{
Count = 2,
Price = 12 // special price, if 2 myObjects were added to the Dictionary
};
myObjectA.Discount = discountA;
1) I fill the items Dictionary and pass it to the Handler method:
private decimal _totalDiscountedValue { get; set; } = 0;
if (!_items.ContainsKey(myObject))
{
_items.Add(myObject, 1);
}
else
{
_items[myObject]++;
}
_totalDiscountedValue += _discountHandler.CalculateDiscount(_items);
2) In my Handler I try to sum up all the discount values, once a counter rule is full filled. But here I'm struggling unfortunately:
public class DiscountHandler : DiscountHandler
{
private decimal _totalDiscount { get; set; } = 0;
public override decimal CalculateDiscount(IDictionary<myObject, int> items)
{
if (items == null) throw new ArgumentNullException(nameof(items));
// I'm struggeling here:
// check if Dictionary[i].Dicount.Count = Dictionary.Value
// then _totalDiscount += Dictionary[i].Discount.Price
return _totalDiscount;
}
}
Do you know how to solve this issue, or do you have an idea on how to possibly solve this?
Thank you very much !!
You could just iterate through the Dictionary using foreach as follows:
public override decimal CalculateDiscount(IDictionary<myObject, int> items)
{
if (items == null) throw new ArgumentNullException(nameof(items));
foreach (var kvp in items)
{
if (kvp.Key.Discount.Count == kvp.Value)
_totalDiscount += kvp.Key.Discount.Price;
}
return _totalDiscount;
}
Using Linq
//check if yourDictonary is not null
var sum = yourDictonary.Select(x => x.Key.Discount.Count == x.Value).Sum(x => x.Value)
IF I understood the problem properly, maybe doing this would work
foreach (var item in items)
{
if (item.Key.Discount.Count == item.Value)
_totalDiscount += item.Key.Discount.Price;
}
return __totalDiscount;
Is there any library out there that can serialize objects with array properties to .csv?
Let's say I have this model:
public class Product
{
public string ProductName { get; set; }
public int InStock { get; set; }
public double Price { get; set; }
...
public string[] AvailableVariants { get; set; }
}
Would something like that be possible to do?
Edit: I need to present some data in a csv/excel format. The thing is, I'm not sure if there is a simple way of achieving what I want with CSV serialization libraries or if I should rather focus on writing an Excel native file.
An example of result I'm looking for:
Product Name In Stock Price Variants
ABC 241 200 Normal
CAB 300 300 Normal
Red
Blue
CBA 125 100 Normal
White
Awesome
Red
ACB 606 75 Normal
Small
Large
X-Large
What would be the most efficient way to do this?
I'm not aware of any libraries that will do this, here's a console example of how I'd approach writing/reading from a CSV:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace TestingProduct
{
class TestingProduct
{
public class Product
{
public string ProductName { get; set; }
public int InStock { get; set; }
public double Price { get; set; }
public string[] AvailableVariants { get; set; }
public override string ToString() => $"{ProductName},{InStock},{Price}{(AvailableVariants?.Length > 0 ? "," + string.Join(",", AvailableVariants) : "")}";
public static Product Parse(string csvRow)
{
var fields = csvRow.Split(',');
return new Product
{
ProductName = fields[0],
InStock = Convert.ToInt32(fields[1]),
Price= Convert.ToDouble(fields[2]),
AvailableVariants = fields.Skip(3).ToArray()
};
}
}
static void Main()
{
var prod1 = new Product
{
ProductName = "test1",
InStock= 2,
Price = 3,
AvailableVariants = new string[]{ "variant1", "variant2" }
};
var filepath = #"C:\temp\test.csv";
File.WriteAllText(filepath, prod1.ToString());
var parsedRow = File.ReadAllText(filepath);
var parsedProduct = Product.Parse(parsedRow);
Console.WriteLine(parsedProduct);
var noVariants = new Product
{
ProductName = "noVariants",
InStock = 10,
Price = 10
};
var prod3 = new Product
{
ProductName = "test2",
InStock= 5,
Price = 5,
AvailableVariants = new string[] { "variant3", "variant4" }
};
var filepath2 = #"C:\temp\test2.csv";
var productList = new List<Product> { parsedProduct, prod3, noVariants };
File.WriteAllText(filepath2, string.Join("\r\n", productList.Select(x => x.ToString())));
var csvRows = File.ReadAllText(filepath2);
var newProductList = new List<Product>();
foreach (var csvRow in csvRows.Split(new string[] { "\r\n" }, StringSplitOptions.None))
{
newProductList.Add(Product.Parse(csvRow));
}
newProductList.ForEach(Console.WriteLine);
Console.ReadKey();
}
}
}
This code will work with a class that has a single object array property. Do you need something that can handle an object with multiple array properties?
I have written some kind of library to write csv files, have a look:
public static class CsvSerializer
{
public static bool Serialize<T>(string path, IList<T> data, string delimiter = ";")
{
var csvBuilder = new StringBuilder();
var dataType = typeof(T);
var properties = dataType.GetProperties()
.Where(prop => prop.GetCustomAttribute(typeof(CsvSerialize)) == null);
//write header
foreach (var property in properties)
{
csvBuilder.Append(property.Name);
if (property != properties.Last())
{
csvBuilder.Append(delimiter);
}
}
csvBuilder.Append("\n");
//data
foreach (var dataElement in data)
{
foreach (var property in properties)
{
csvBuilder.Append(property.GetValue(dataElement));
if (property != properties.Last())
{
csvBuilder.Append(delimiter);
}
}
csvBuilder.Append("\n");
}
File.WriteAllText(path, csvBuilder.ToString());
return true;
}
}
public class CsvSerialize : Attribute
{
}
Lets pretend you want to serialize following class:
public class MyDataClass
{
[CsvSerialize]
public string Item1 {get; set;}
[CsvSerialize]
public string Item2 {get; set;}
}
Then just do:
public void SerializeData(IList<MyDataClass> data)
{
CsvSerializer.Serialize("C:\\test.csv", data);
}
It takes a IList of your class and writes a csv.
It cant serialize arrays but that would be easy to implement.
I have a text file with below format data
[
{
"SponsorID": 1,
"FirstBAID": 7395836
},
{
"SponsorID": 2,
"FirstBAID": 3509279,
"SecondBAID": 2947210
},
{
"SponsorID": 3,
"FirstBAID": 1776294,
"SecondBAID": 6503843
},
{
"SponsorID": 4,
"FirstBAID": 8014528,
"SecondBAID": 6203155
},
{
"SponsorID": 5,
"FirstBAID": 5968769,
"SecondBAID": 7410195,
"ThirdBAID":8950170,
}
]
I want to read this data as a List & then i need to query by SponsorID.
I have created a class like this
public class SponsorInfo
{
public decimal SponsorID { get; set; }
public decimal FirstBAID { get; set; }
public decimal SecondBAID { get; set; }
public decimal ThirdBAID { get; set; }
}
Now how can i read text file data & bind SponsorInfo class ?
Install Newtonsoft.Json nuget package from NuGet package manager console:
PM> Install-Package Newtonsoft.Json
Then:
var jsonText = File.ReadAllText("filepath");
var sponsors = JsonConvert.DeserializeObject<IList<SponsorInfo>>(jsonText);
To query on SponsorID you can use LINQ:
var sponsor5 = sponsors.FirstOrDefault(x => x.SponsorID == 5);
If you often need a lookup by SponsorID, you could convert the result to a dictionary where the key is the SponsorID. This will improve performance as it doesn't need to enumerate through the entire list for each lookup. I also suggest you change the type of SponsorID to an int instead of a decimal.
var sponsorsById = sponsors.ToDictionary(x => x.SponsorID);
Then you can easily access it like:
if (sponsorsById.ContainsKey(5))
var sponsor5 = sponsorsById[5];
You need to install Newtonsoft.Json and then you need use it:
using Newtonsoft.Json;
class Program
{
public void LoadJson()
{
using (StreamReader r = new StreamReader("file.json"))
{
string json = r.ReadToEnd();
List<SponsorInfo> items = JsonConvert.DeserializeObject<List<SponsorInfo>>(json);
}
}
public class SponsorInfo
{
public decimal SponsorID { get; set; }
public decimal FirstBAID { get; set; }
public decimal SecondBAID { get; set; }
public decimal ThirdBAID { get; set; }
}
static void Main(string[] args)
{
dynamic array = JsonConvert.DeserializeObject(json);
foreach (var item in array)
{
Console.WriteLine("{0} {1}", item.temp, item.vcc);
}
}
}
Extend the class by creating a list object
public class SponsorInfo
{
public decimal SponsorID { get; set; }
public decimal FirstBAID { get; set; }
public decimal SecondBAID { get; set; }
public decimal ThirdBAID { get; set; }
}
public class SponsorInfoList
{
public Dictionary<string, SponsorInfo> SIList { set; get; }
}
Deserialize the file as,
var obj = JsonConvert.DeserializeObject<SIList >(File.ReadAllText(FileName));
Then you can read it,
foreach(var listItem in res.SIList )
{
Console.WriteLine("SponsorID ={0}, FirstBAID ={1}, SecondBAID ={2}, ThirdBAID ={3}", listItem.SponsorID, listItem.FirstBAID, listItem.SecondBAID, listItem.ThirdBAID );
}
There may be syntactical errors but the approach remains same.
Feel free to leave a message!
You need to deserialize into your object like:
Sponsor spon = JsonConvert.DeserializeObject<Sponsor>(json);
Broadcom
Connection Name: Local Area Connection
DHCP Enabled: No
IP address(es)
[01]: abc.de.fg.h
I would like to put this into a Dictionary<string, Dictionary <string, string>>
So Broadcom would be the key in the first dictionary and the rest would be value the value. The value should be stored as a dictionary where Connection Name is the key and Local Area Connection is the value. How do I go about doing that? I am new to programming. Thanks for the help.
Please do yourself a great favour and use a class for that, because in two months time you will not understand your code:
public class WhatIsThis {
private List<IPAddress> ipAddresses = new List<IPAddress>();
public string Name { get; set; }
public string ConnectionName { get; set; }
public bool DHCPEnabled { get; set; }
public List<IPAddress> IPAddresses { get return ipAddresses; }
}
Now you can keep a List<WhatIsThis> around. If you have justa few of them, the O(n) cost of looking a value up is negligible. If you want a lookup by name in O(1) fashion, you can map to a Dictionary as such:
var dict = listOfWhatisThis.ToDictionary(k=>k.Name, v=>v}
Gives you a Dictionary of type Dictionary<string,WhatIsThis> . What do you think, will you understand this better in 2 weeks time, or rather your scary Dictionary?
http://msdn.microsoft.com/en-us/library/bb531208.aspx
var dic = new dictionary<string, dictionary <string, string>>(
{"Broadcom",
new dictionary <string, string>(
{
{"Connection Name", "Local Area Connection"},
{"DHCP Enabled", "No"},
{"IP address(es) [01]", "abc.de.fg.h"}
}
)
}
);
My only concern would be that the IP Addresses should themselves be in a dictionary within the settings for a connection, not a single string, and thus the sub-dictionary would need to be dictionary<string, object>.
Similar to flq's answer, here's a more comprehensive solution which makes both subdictionaries:
using System.Collections.Generic;
using System.Linq;
namespace LINQDictionaryDemo
{
public class IPAddress
{
public int Index { get; private set; }
public string Value { get; private set; }
public IPAddress(int Index, string Value)
{
this.Index = Index;
this.Value = Value;
}
}
public class NetworkConnection
{
public string Name { get; set; }
public string ConnectionName { get; set; }
public bool DHCPEnabled { get; set; }
public List<IPAddress> IPAddresses { get; set; }
public Dictionary<string, object> ToDictionary()
{
return new Dictionary<string, object>
{
{ "ConnectionName", ConnectionName }
, { "DHCPEnabled", DHCPEnabled.ToString() }
, {"IPAddresses", IPAddresses.ToDictionary(k => k.Index, v => v.Value)}
};
}
}
public static class Demo
{
public static void Run()
{
var lnc = new List<NetworkConnection>
{
new NetworkConnection
{
Name = "Broadcom",
ConnectionName = "Local Area Connection",
DHCPEnabled = false,
IPAddresses = new List<IPAddress> {new IPAddress(1, "abc.de.fg.h")}
}
};
var dic = lnc.ToDictionary(k => k.Name, v => v.ToDictionary());
}
}
}