I have a simple question. During i create a dictionary in c#, half of the dictionary contains question marks. Here is my situration:
SourceCode:
/// <summary>
/// Get Member
/// </summary>
/// <param name="Binder"></param>
/// <param name="Result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder Binder, out object Result)
{
if (Binder.Name == "AsDictionary")
{
IDictionary<string, string> cValues = new Dictionary<string, string>();
foreach (var cValue in myValues)
{
string cVal = "";
if (cValue.Value == null)
{
cVal = "";
}
else
{
cVal = cValue.Value.ToString();
}
cValues.Add(cValue.Key, cVal);
}
Result = cValues;
return true;
}
int cCount = myValues.Where(Item => Item.Key.ToLower() == Binder.Name.ToLower()).ToList().Count;
if (cCount == 0)
{
Result = null;
return false;
}
else
{
Result = myValues.Where(Item => Item.Key.ToLower() == Binder.Name.ToLower()).First().Value;
}
return true;
}
myValues is also a ObservableCollection:
private ObservableCollection<DynamicSqlValue> myValues;
DynamicSqlValue is a very simple class:
public class DynamicSqlValue
{
public string Key
{
get;
set;
}
public object Value
{
get;
set;
}
}
Thank you for your help!
Must be some kind of visual studio issue or maybe you're running out of memory. Have you tried logging the values?
I would go a step farther than Patryk and ask why you aren't doing myValues.ToDictionary. Also, the .Where, .ToList, and .Count should be as simple as:
myValues.Any(kvp => kvp.Key.Equals(Binder.Name, StringComparison.InvariantCultureIgnoreCase))
That function should really only be like 4 or 5 lines of code.
It's not answer for Your question (sorry for that) but i have some refactor tip of Your code:
Make Your foreach like that:
foreach (var cValue in myValues)
{
cValues.Add(cValue.Key, cValue.Value != null ? cValue.Value.ToString() : string.Empty);
}
And create local variable from linq query:
var binderMyValues = myValues.Where(Item => Item.Key.ToLower() == Binder.Name.ToLower());
to use it like e.g.
int cCount = binderMyValues.ToList().Count;
or...
Result = binderMyValues.First().Value;
Related
Im able to get the body of the email via imap in c#, but its inclusive html tags like this...
<html><head></head><body><div style="font-family: Verdana;font-size: 12.0px;"><div>asd</div></div></body></html>
I would only want "asd" from this...
This is what I use to get it with the html tags:
foreach (var a in messages)
{
//MessageBox.Show($"{a.Body}");
rec_body.AppendText( a.Body);
}
Im using S22.Imap
Install package from Nuget: HTML AgilityPack
You can try;
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
string s = doc.DocumentNode.SelectSingleNode("//body").InnerText;
You don't mention which IMAP library you are using, but if you are using MailKit, you could start by more-or-less cloning MimeKit's HtmlTextPreviewer class.
For example (untested):
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using MimeKit.Text;
namespace MyNamespace {
/// <summary>
/// A text extractor for HTML content.
/// </summary>
/// <remarks>
/// A text extractor for HTML content.
/// </remarks>
public class HtmlTextExtractor
{
/// <summary>
/// Initialize a new instance of the <see cref="HtmlTextExtractor"/> class.
/// </summary>
/// <remarks>
/// Creates a new text extractor for HTML.
/// </remarks>
public HtmlTextExtractor ()
{
}
static bool IsWhiteSpace (char c)
{
return char.IsWhiteSpace (c) || (c >= 0x200B && c <= 0x200D);
}
static bool Append (StringBuilder extracted, string value, ref bool lwsp)
{
int i;
for (i = 0; i < value.Length; i++) {
if (IsWhiteSpace (value[i])) {
if (!lwsp) {
extracted.Append (' ');
lwsp = true;
}
} else {
extracted.Append (value[i]);
lwsp = false;
}
}
return false;
}
sealed class HtmlTagContext
{
public HtmlTagContext (HtmlTagId id)
{
TagId = id;
}
public HtmlTagId TagId {
get;
}
public int ListIndex {
get; set;
}
public bool SuppressInnerContent {
get; set;
}
}
static void Pop (IList<HtmlTagContext> stack, HtmlTagId id)
{
for (int i = stack.Count; i > 0; i--) {
if (stack[i - 1].TagId == id) {
stack.RemoveAt (i - 1);
break;
}
}
}
static bool ShouldSuppressInnerContent (HtmlTagId id)
{
switch (id) {
case HtmlTagId.OL:
case HtmlTagId.Script:
case HtmlTagId.Style:
case HtmlTagId.Table:
case HtmlTagId.TBody:
case HtmlTagId.THead:
case HtmlTagId.TR:
case HtmlTagId.UL:
return true;
default:
return false;
}
}
static bool SuppressContent (IList<HtmlTagContext> stack)
{
int lastIndex = stack.Count - 1;
return lastIndex >= 0 && stack[lastIndex].SuppressInnerContent;
}
HtmlTagContext GetListItemContext (IList<HtmlTagContext> stack)
{
for (int i = stack.Count; i > 0; i--) {
var ctx = stack[i - 1];
if (ctx.TagId == HtmlTagId.OL || ctx.TagId == HtmlTagId.UL)
return ctx;
}
return null;
}
/// <summary>
/// Extract text from HTML input.
/// </summary>
/// <remarks>
/// Gets the extracted text from HTML input.
/// </remarks>
/// <param name="reader">A reader for HTML text.</param>
/// <returns>A string representing the extracted text.</returns>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="reader"/> is <c>null</c>.
/// </exception>
public string Extract (TextReader reader)
{
if (reader == null)
throw new ArgumentNullException (nameof (reader));
var tokenizer = new HtmlTokenizer (reader) { IgnoreTruncatedTags = true };
var stack = new List<HtmlTagContext> ();
var extracted = new StringBuilder ();
var prefix = string.Empty;
HtmlTagContext ctx;
HtmlAttribute attr;
bool body = false;
bool full = false;
bool lwsp = true;
HtmlToken token;
while (!full && tokenizer.ReadNextToken (out token)) {
switch (token.Kind) {
case HtmlTokenKind.Tag:
var tag = (HtmlTagToken) token;
if (!tag.IsEndTag) {
if (body) {
switch (tag.Id) {
case HtmlTagId.Image:
if ((attr = tag.Attributes.FirstOrDefault (x => x.Id == HtmlAttributeId.Alt)) != null) {
Append (extracted, prefix + attr.Value, ref lwsp);
prefix = string.Empty;
}
break;
case HtmlTagId.LI:
if ((ctx = GetListItemContext (stack)) != null) {
if (ctx.TagId == HtmlTagId.OL) {
Append (extracted, $" {++ctx.ListIndex}. ", ref lwsp);
prefix = string.Empty;
} else {
//Append (extracted, " \u2022 ", ref lwsp);
prefix = " ";
}
}
break;
case HtmlTagId.Br:
case HtmlTagId.P:
extracted.Append ('\n');
prefix = string.Empty;
lwsp = true;
break;
}
if (!tag.IsEmptyElement) {
ctx = new HtmlTagContext (tag.Id) {
SuppressInnerContent = ShouldSuppressInnerContent (tag.Id)
};
stack.Add (ctx);
}
} else if (tag.Id == HtmlTagId.Body && !tag.IsEmptyElement) {
body = true;
}
} else if (tag.Id == HtmlTagId.Body) {
stack.Clear ();
body = false;
} else {
Pop (stack, tag.Id);
}
break;
case HtmlTokenKind.Data:
if (body && !SuppressContent (stack)) {
var data = (HtmlDataToken) token;
Append (extracted, prefix + data.Data, ref lwsp);
prefix = string.Empty;
}
break;
}
}
if (lwsp && extracted.Length > 0)
extracted.Length--;
return extracted.ToString ();
}
/// <summary>
/// Extract text from HTML input.
/// </summary>
/// <remarks>
/// Gets the extracted text from HTML input.
/// </remarks>
/// <param name="reader">A reader for HTML text.</param>
/// <returns>A string representing the extracted text.</returns>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="reader"/> is <c>null</c>.
/// </exception>
public string Extract (string input)
{
if (input == null)
throw new ArgumentNullException (nameof (input));
using (var reader = new StringReader (input))
return Extract (reader);
}
}
}
To use this class:
var extractor = new HtmlTextExtractor ();
var text = extractor.Extract ("<html><body>This is some text.</body></html>");
The class I've written above also tries to handle HTML list tags as well, so you may find that nice :)
You can try the following code to get email body without html via IMAP.
First, please install nuget-package ImapX.
Second, you can try the following code to read the email body information from the gmail inbox.
var client = new ImapX.ImapClient("imap.gmail.com", 993, true);
bool t=client.Connect();
bool m=client.Login("Username#gmail.com", "userpassword");
var inbox= client.Folders.Inbox;
var messages = inbox.Search("ALL", ImapX.Enums.MessageFetchMode.Full);
foreach (var item in messages)
{
Console.WriteLine(item.Body.Text);
Console.WriteLine("*****************");
}
First email in gmail website:
First email in console:
I have a ServiceField class which contains FieldId and Value as two properties .
public class ServiceField
{
public int FieldId { get; set; }
public string Value { get; set; }
}
/// <summary>
/// This method is used to assign value to HajjPackageFields class
/// </summary>
/// <param name="hajPackageObj">HajjPackageFields whose value needs to be updated</param>
private void UpdateDetailFieldsValue(HajjPackageFields hajPackageObj)
{
foreach (ServiceField field in GetPackageDetails(hajPackageObj.PackageId))
{
if (field.FieldId == (int)HajjServiceFieldsEnum.AccomodationView)
{
hajPackageObj.AccomodationView = field.Value == "1";
}
else if (field.FieldId == (int)HajjServiceFieldsEnum.AirTicket)
{
hajPackageObj.AirTicket = field.Value == "1";
}
}
}
Problem is if any new property add in HajjPackageField class than i have to modify my UpdateDetailFieldsValue method which is against the open close principle. Is there any other proper way to achieve this task ?
If I clearly understood you, reflection will help to solve problem:
private void UpdateDetailFieldsValue(HajjPackageFields hajPackageObj)
{
var pairs = new Dictionary<int, string>();
foreach (var enumVal in typeof(HajjServiceFieldsEnum).GetEnumValues())
pairs[(int)(HajjServiceFieldsEnum)enumVal] = typeof(HajjServiceFieldsEnum).GetEnumName(enumVal);
foreach (ServiceField field in GetPackageDetails(hajPackageObj.PackageId))
typeof(HajjPackageFields).GetProperty(pairs[field.FieldId])
?.SetValue(hajPackageObj, field.Value == "1");
}
Say I have:
public class SPListItem
{
public override object this[string fieldName]
{
get
{
return this.GetValue(fieldName);
}
set
{
this.SetValue(fieldName, value, !this.HasExternalDataSource);
}
}
}
public class Bar
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
public int Prop3 { get; set; }
}
is there any way I can do:
var fooInst = new SPListItem();
Bar barInst = (Bar)fooInst // or maybe Bar.FromFoo(Foo f) if handling the cast is not possible
and then have:
barInst.Prop1 give me the equivalent of:
fooInst["Prop"];
Without implementing the getters and setters for every property in Bar?
Aaaaaand, here we go. This class generates entities from your lists.
From: https://justsharepointthings.wordpress.com/2015/09/10/sharepoint-generate-c-poco-classes-from-an-existing-definition/
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.SharePoint;
namespace Code_Generation {
/// <summary>
/// Generates List object entities from a site connection.
/// </summary>
public class SPListPocoGenerator {
string parentDir = "GeneratedListPOCO/";
string hiddenDir = "GeneratedListPOCO/HiddenLists/";
private class PropertyString {
private string _propStr;
public PropertyString(string propStr) {
_propStr = propStr;
_properties = new Dictionary < string, string > ();
}
private Dictionary < string, string > _properties;
public string this[string key] {
get {
return _properties.ContainsKey(key) ? _properties[key] : string.Empty;
}
set {
if (_properties.ContainsKey(key)) {
_properties[key] = value;
} else {
_properties.Add(key, value);
}
}
}
/// <summary>
/// Replaces properties in the format {{propertyName}} in the source string with values from KeyValuePairPropertiesDictionarysupplied dictionary.nce you've set a property it's replaced in the string and you
/// </summary>
/// <param name="originalStr"></param>
/// <param name="keyValuePairPropertiesDictionary"></param>
/// <returns></returns>
public override string ToString() {
string modifiedStr = _propStr;
foreach(var keyvaluePair in _properties) {
modifiedStr = modifiedStr.Replace("{{" + keyvaluePair.Key + "}}", keyvaluePair.Value);
}
return modifiedStr;
}
}
public string _classDefinitionStr = #
"
using System;
using Microsoft.SharePoint;
public class {{EntityName}}
{
private SPListItem listItem;
public {{EntityName}}_InternalProperties InternalProperties
{
get; private set;
}
public {{EntityName}}(SPListItem li)
{
this.listItem = li;
this.InternalProperties = new {{EntityName}}_InternalProperties(this.listItem);
}
{{PropertySections}}
public class {{EntityName}}_InternalProperties
{
private SPListItem listItem;
public {{EntityName}}_InternalProperties(SPListItem li)
{
this.listItem = li;
}
{{HiddenPropertySections}}
{{InternalPropertySections}}
}
}";
private const string _propertySectionStr = "\n\n\t" + #
"public {{PropertyType}} {{PropertyName}}
{ get { return listItem[Guid.Parse("
"{{PropertyId}}"
")] as {{PropertyType}}; }
set { listItem[Guid.Parse("
"{{PropertyId}}"
")] = value; }}";
/// <summary>
/// Gets string identifying the field type
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
private string GetSafeTypeName(SPField field) {
if (field.FieldValueType == null) {
return "object"; //Not going to try to parse it further, this is enough.
}
var type = field.FieldValueType;
if (type.IsValueType) {
return type.FullName + "?";
}
return type.FullName;
}
public void GenerateForWeb(SPWeb web) {
var blackList = new[] {
"Documents", "Form Templates", "Site Assets", "Site Pages", "Style Library"
};
Directory.CreateDirectory(parentDir);
Directory.CreateDirectory(hiddenDir);
foreach(SPList list in web.Lists) {
PropertyString _classDefinition = new PropertyString(_classDefinitionStr);
string entityName = "SPL_" + list.Title.Replace(" ", "");
_classDefinition["EntityName"] = entityName;
foreach(SPField field in list.Fields) {
PropertyString propertySection = new PropertyString(_propertySectionStr);
propertySection["PropertyType"] = GetSafeTypeName(field); //field.FieldValueType.FullName; -> Returning Null often. Thanks, SharePoint!
propertySection["PropertyName"] = field.EntityPropertyName.Replace("_x0020_", "_");
propertySection["PropertyId"] = field.Id.ToString();
if (SPBuiltInFieldId.Contains(field.Id)) _classDefinition["InternalPropertySections"] += propertySection;
else if (field.Hidden) _classDefinition["HiddenPropertySections"] += propertySection;
else _classDefinition["PropertySections"] += propertySection;
}
if (list.Hidden || blackList.Contains(list.Title)) {
File.WriteAllText(hiddenDir + entityName + ".cs", _classDefinition.ToString());
} else {
File.WriteAllText(parentDir + entityName + ".cs", _classDefinition.ToString());
}
}
}
}
}
For my former employer I implemented a DAO pattern for SharePoint. Unfortunately I was not allowed to take the code with me or publish it... I used annotations together with reflection to solve the issues with different names, optional fields, type casting etc. I also wrote a generator for the DTO-objects. But to be honest, it was a quite big effort for something that LINQ might solve in your case. Or writing your classes by hand, or writing a code generator for the getters and setters - it all depends on the size of your project(s).
Before implementing my own DAO I had a quite bad experience with LINQ to SQL, especially when columns were renamed, added or removed I didn't like the behaviour, I also had performance issues using it. That's why I prefered my own DAO pattern, but for easy tasks LINQ might be enough. My experience with LINQ also might be outdated, it was about 7 years ago ;)
You could use an ExpandoObject, which is in the System.Dynamic namespace. Try something like this (untested):
public class SPListItemPropertyMapper
{
private dynamic _expandoObject;
public SPListItemPropertyMapper(SPListItem listItem)
{
_expandoObject = new ExpandoObject();
foreach (SPField field in listItem.Fields)
{
_expandoObject.Add(field.InternalName, listItem.GetFormattedValue(field.InternalName));
}
}
public dynamic FieldValues
{
get { return _expandoObject; }
}
}
Usage:
SPListItem listItem; //your list item here
var propertyMapper = new SPListItemPropertyMapper(listItem);
var title = propertyMapper.FieldValues.Title;
var editor = propertyMapper.FieldValues.Editor;
var created = propertyMapper.FieldValues.Created;
etc. You should consider extending the foreach loop by more logic, to return values based on the field type instead of just using GetFormattedValue.
Given the following classes and data:
public class InnerExample
{
public string Inner1 { get; set; }
}
public class Example
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public List<InnerExample> Inner { get; set; }
}
var a = new Example
{
Property1 = "Foo",
Property2 = "Bar",
Inner = new List<InnerExample>
{
new InnerExample
{
Inner1 = "This is the value to change"
}
}
};
Is there any way to access the innermost data by path?
Is there any way to say...
a["Inner[0].Inner1"] = "New value"
In this particular case, I know for a fact that I will never be accessing a key that does not exist, so I'm not overly concerned about error checking.
(Sorry if this has been asked before. I did a few searches but quickly ran out of keywords to try.)
There's nothing built-in, but it's possible to do (even though it won't be trivial).
What you want is to add an indexer to the class Example. Inside the indexer you will have to parse the provided "property path" into steps and use reflection to resolve the target property step by step.
For example, after parsing Inner[0].Inner1 into three distinct steps (fetch Inner, then from that fetch [0], then from that Inner1) you would have a loop that goes somewhat like this:
// This works only with plain (non-indexed) properties, no error checking, etc.
object target = this;
PropertyInfo pi = null;
foreach (var step in steps)
{
pi = target.GetType().GetProperty(step);
target = pi.GetValue(target);
}
// And now you can either return target (on a get) or use pi.SetValue (on a set)
Thanks to the basic advice you gave me, Jon, I came up with a solution that works for my case.
There is no error checking
You must be setting a property, not an array element.
I'm sure there are more efficient ways to do this... I'm far from an expert on reflection.
/// <summary>
/// Take an extended key and walk through an object to update it.
/// </summary>
/// <param name="o">The object to update</param>
/// <param name="key">The key in the form of "NestedThing.List[2].key"</param>
/// <param name="value">The value to update to</param>
private static void UpdateModel(object o, string key, object value)
{
// TODO:
// Make the code more efficient.
var target = o;
PropertyInfo pi = null;
// Split the key into bits.
var steps = key.Split('.').ToList();
// Don't walk all the way to the end
// Save that for the last step.
var lastStep = steps[steps.Count-1];
steps.RemoveAt(steps.Count-1);
// Step through the bits.
foreach (var bit in steps)
{
var step = bit;
string index = null;
// Is this an indexed property?
if (step.EndsWith("]"))
{
// Extract out the value of the index
var end = step.IndexOf("[", System.StringComparison.Ordinal);
index = step.Substring(end+1, step.Length - end - 2);
// and trim 'step' back down to exclude it. (List[5] becomes List)
step = step.Substring(0, end);
}
// Get the new target.
pi = target.GetType().GetProperty(step);
target = pi.GetValue(target);
// If the target had an index, find it now.
if (index != null)
{
var idx = Convert.ToInt16(index);
// The most generic way to handle it.
var list = (IEnumerable) target;
foreach (var e in list)
{
if (idx ==0)
{
target = e;
break;
}
idx--;
}
}
}
// Now at the end we can apply the last step,
// actually setting the new value.
if (pi != null || steps.Count == 0)
{
pi = target.GetType().GetProperty(lastStep);
pi.SetValue(target, value);
}
}
I have 2 excel files that I have converted into lists. The 1st file has a complete list of all items that I need. However, the 2nd list has a small list of items that need to be changed in the 1st list.
Here's how my 1st list is constructed:
IEnumerable<ExcelRow> queryListA = from d in datapullList
select new ExcelRow
{
Company = d.GetString(0),
Location = d.GetString(1),
ItemPrice = d.GetString(4),
SQL_Ticker = d.GetString(15)
};
The 2nd list is constructed in a very similar way:
IEnumerable<ExcelRow> queryListB = from dupes in dupespullList
select new ExcelRow
{
Company = d.GetString(0),
Location = d.GetString(1),
NewCompany = d.GetString(4)
};
So, if there is a company from a particular location in 1st list that matches 2nd list, then the company gets changed to the newcompany name.
Then, my final list should have everything in 1st list but with the changes specified from 2nd list.
I've been struggling with this for a few days now. Let me know if you need more details.
[Update:] I'm pretty new to LINQ and C#. I've found this code on the web regarding Excel reader for Office 2003. How can I create the 1 list (stated above) from all the following classes?
My ExcelRow class:
class ExcelRow
{
List<object> columns;
public ExcelRow()
{
columns = new List<object>();
}
internal void AddColumn(object value)
{
columns.Add(value);
}
public object this[int index]
{
get { return columns[index]; }
}
public string GetString(int index)
{
if (columns[index] is DBNull)
{
return null;
}
return columns[index].ToString();
}
public int Count
{
get { return this.columns.Count; }
}
}
My ExcelProvider class:
class ExcelProvider : IEnumerable<ExcelRow>
{
private string sheetName;
private string filePath;
private string columnName1;
private string columnName2;
private List<ExcelRow> rows;
public ExcelProvider()
{
rows = new List<ExcelRow>();
}
public static ExcelProvider Create(string filePath, string sheetName, string columnName1, string columnName2)
{
ExcelProvider provider = new ExcelProvider();
provider.sheetName = sheetName;
provider.filePath = filePath;
provider.columnName1 = columnName1;
provider.columnName2 = columnName2;
return provider;
}
private void Load()
{
string connectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties= ""Excel 8.0;HDR=YES;IMEX=1""";
connectionString = string.Format(connectionString, filePath);
rows.Clear();
using (OleDbConnection conn = new OleDbConnection(connectionString))
{
try
{
conn.Open();
using (OleDbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = string.Format("SELECT * FROM [{0}$] WHERE {1} IS NOT NULL AND {2} <> \"{3}\"", sheetName, columnName1, columnName2, null);
using (OleDbDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
ExcelRow newRow = new ExcelRow();
for (int count = 0; count < reader.FieldCount; count++)
{
newRow.AddColumn(reader[count]);
}
rows.Add(newRow);
}
}
}
}
catch (Exception ex)
{ throw ex; }
finally
{
if (conn.State == System.Data.ConnectionState.Open)
conn.Close();
}
}
}
public IEnumerator<ExcelRow> GetEnumerator()
{
Load();
return rows.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
Load();
return rows.GetEnumerator();
}
}
So, using all this logic, how can I solve my problem?
//first create a dictionary of comapny whose name has been changed
var dict = queryListB.ToDictionary(x => x.Company, y => y.NewCompany);
//loop on the first list and do the changes in the first list
queryListA.ForEach( x =>
{
if(dict.Keys.Contains(x.Company))
x.Company = dict[x.Company];
});
Loop through queryListA and see if there is a matching company in queryListB. If so, then update the Company property.
Here's the code:
foreach (var companyA in queryListA)
{
var companyBMatch = queryListB.FirstOrDefault(x => x.Company == companyA.Company && x.Location == companyA.Location);
if (companyBMatch != null)
companyA.Company = companyBMatch.NewCompany;
}
I'm sure you can write simpler code to achieve the same goal but I've gone for a way that reduces the number of times you have to iterate through the first and second lists. If performance isn't an issue a simpler method that just searches the dupespullList for each element in datapullList might be appropriate.
var excelRowCreator = new ExcelRowCreator(dupespullList);
var finalRows = excelRowCreator.CreateExcelRows(datapullList);
// ...
public class ExcelRowCreator
{
/// <summary>
/// First key is company name, second is location
/// and final value is the replacement name.
/// </summary>
private readonly IDictionary<string, IDictionary<string, string>> nameReplacements;
/// <summary>
/// I don't know what type of objects your initial
/// lists contain so replace T with the correct type.
/// </summary>
public ExcelRowCreator(IEnumerable<T> replacementRows)
{
nameReplacements = CreateReplacementDictionary(replacementRows);
}
/// <summary>
/// Creates ExcelRows by replacing company name where appropriate.
/// </summary>
public IEnumerable<ExcelRow> CreateExcelRows(IEnumerable<T> inputRows)
{
// ToList is here so that if you iterate over the collection
// multiple times it doesn't create new excel rows each time
return inputRows.Select(CreateExcelRow).ToList();
}
/// <summary>
/// Creates an excel row from the input data replacing
/// the company name if required.
/// </summary>
private ExcelRow CreateExcelRow(T data)
{
var name = data.GetString(0);
var location = data.GetString(1);
IDictionary<string, string> replacementDictionary;
if (nameReplacements.TryGetValue(name, out replacementDictionary))
{
string replacementName;
if (replacementDictionary.TryGetValue(location, out replacementName))
{
name = replacementName;
}
}
return new ExcelRow
{
Company = name,
Location = location,
ItemPrice = data.GetString(4),
SQL_Ticker = data.GetString(15)
};
}
/// <summary>
/// A helper method to create the replacement dictionary.
/// </summary>
private static IDictionary<string, IDictionary<string, string>> CreateReplacementDictionary(IEnumerable<T> replacementRows)
{
var replacementDictionary = new Dictionary<string, IDictionary<string, string>>();
foreach (var dupe in replacementRows)
{
var name = dupe.GetString(0);
IDictionary<string, string> locationReplacements;
if (!replacementDictionary.TryGetValue(name, out locationReplacements))
{
locationReplacements = new Dictionary<string, string>();
replacementDictionary[name] = locationReplacements;
}
locationReplacements[dupe.GetString(1)] = dupe.GetString(4);
}
return replacementDictionary;
}
}
UPDATE : Packaged as a class and written in visual studio so there shouldn't be any grammatical errors.