The code below contains a foreach loop that loops through a list of string collection which contains XML. While enumerating through the collection, it reads the question and answer elements and adds them to the list collection. I need to ensure that no repeated question is added to the list collection.
The code below questionnaire.QuestionAnswers.Add(fataQuestionsAnswers) adds the elements to the list collection. The problem that I am facing is that the repeated questions are getting added to the list. I tried to put the following condition that is:
if (questionnaire.QuestionAnswers.Find(a => a.Question != fataQuestionsAnswers.Question) == null)
but that doesn't seem to work.
var fataQuestionnaireData = DbAccess.GetFatcaQuestionnaire(contactId);
if (fataQuestionnaireData != null)
{
var questionnaire = new FatcaQuestionnaire();
foreach (var fatcaQuestionnaire in fataQuestionnaireData)
{
//var QData = GetObjectFromStream<FatcaQuestionnaire>fataQuestionnaireData);
//FatcaQuestionnaire.Deserialize(fataQuestionnaireData);
XDocument doc = XDocument.Parse(fatcaQuestionnaire.ToString(CultureInfo.InvariantCulture));
// w.WriteLine("The value of doc" + doc);
doc.Descendants("QuestionAnswer").ToList().ForEach(questionAnswer =>
{
var fataQuestionsAnswers = new QuestionAnswers();
{
//var questionAnswer = qa.Element("QuestionAnswer");
var questionElement = questionAnswer.Element("Question");
if (questionElement != null )
fataQuestionsAnswers.Question = questionElement.Value;
//if (questionElement != null)
// w.WriteLine("The value of questionElement" + questionElement.Value);
var answerElement = questionAnswer.Element("Answer");
if (answerElement != null)
fataQuestionsAnswers.Answer = answerElement.Value;
//if (answerElement != null)
// w.WriteLine("The value of answerElement" + answerElement.Value);
var sqa = questionAnswer.Element("SubQuestionAnswer");
if (sqa != null)
{
var subQuestionElement = sqa.Element("Question");
if (subQuestionElement != null)
fataQuestionsAnswers.SubQuestionAnswer.Question = subQuestionElement.Value;
//if (subQuestionElement != null)
// w.WriteLine("The value of answerElement" + subQuestionElement.Value);
var subAnswerElement = sqa.Element("Answer");
if (subAnswerElement != null)
fataQuestionsAnswers.SubQuestionAnswer.Answer = subAnswerElement.Value;
//if (subQuestionElement != null)
// w.WriteLine("The value of answerElement" + subQuestionElement.Value);
}
if (questionnaire.QuestionAnswers.Find(a => a.Question != fataQuestionsAnswers.Question) == null)
questionnaire.QuestionAnswers.Add(fataQuestionsAnswers);
//fatcaQuestionsList.Add(fataQuestionsAnswers);
}
fatca.Questionnaire.Add(fataQuestionsAnswers);
});
}
}
You have your condition wrong, you are looking for questionanswers where the question does not match, you should be looking where they do match and checking that the result is null. (switch the != with ==)
It should be
if (questionnaire.QuestionAnswers.Find(a => a.Question == fataQuestionsAnswers.Question) == null)
However I would change it to Any() as it is a bit nearer and easier to read, it returns a true if one of the items in your list matches the condition that you specify.
if(!questionnaire.QuestionAnswers.Any(a => a.Question == fataQuestionAnswers.Question)) {
questionnaire.QuestionAnswers.Add(fataQuestionsAnswers);
}
Use Any instead
if(!questionnaire.QuestionAnswers.Any(a => a.Question == fataQuestionsAnswers.Question))
{
questionnaire.QuestionAnswers.Add(fataQuestionsAnswers);
}
Is it possible there's trailing spaces / casing differences?
if(!questionnaire.QuestionAnswers.Any(a => a.Question.Trim().Equals(fataQuestionsAnswers.Question.Trim(), StringComparison.OrdinalIgnoreCase)))
{
questionnaire.QuestionAnswers.Add(fataQuestionsAnswers);
}
Related
For some reason, my code doesn't seem to reach the last few lines.
I've added the stop point and the return point in the code.
I don't see anything wrong with the data source I use on that row whenever I check it out in debug.
The code seems to set the value there and jumps back to the top of the foreach loop.
foreach (DataGridViewRow row in dataGridView1.Rows) {
//We check if the user has already filled in some info
return point if (row.Cells[7].Value != null && row.Cells[6].Value != null && !askedTheUser)
{
//trigger for message if you want to replace them
Message m = new Message("There is already info present in the category and or size dropdown. Would you like to overwrite this?", "Overwrite", "YESNO");
if (m.Awnser) {
overwrite = true;
}
askedTheUser = true;
}
DataGridViewComboBoxCell cat = (DataGridViewComboBoxCell)row.Cells[6];
string toMatch = row.Cells[3].Value.ToString();
//Now we will start matching
//First we try to match with the package ( if that is filled in )
if (row.Cells[5].Value != null && (string)row.Cells[5].Value != "") {
toMatch = row.Cells[5].Value.ToString();
matchWithPackage = true;
}
Regex re = new Regex(#"([a-zA-Z]+)(\d+)");
Match result = re.Match(toMatch);
string alphaPart = result.Groups[1].Value;
string numberPart = result.Groups[2].Value;
Datagridview dgv = new Datagridview();
if (numberPart.Length < 4) {
numberPart = numberPart.PadLeft(4, '0');
}
#if DEBUG
Console.WriteLine(numberPart);
#endif
//Matching the category
if (CHIP != null && CHIP.Contains(alphaPart))
{
cat.Value = "CHIP";
}
else if (SOJ != null && SOJ.Contains(alphaPart))
{
cat.Value = "SOJ";
}
else if (PLCC != null && PLCC.Contains(alphaPart))
{
cat.Value = "PLCC";
}
else if (QFP != null && QFP.Contains(alphaPart))
{
cat.Value = "QFP";
}
else if (SOP != null && SOP.Contains(alphaPart))
{
cat.Value = "SOP";
}
else {
if (cat.Value != "") {
cat.Value = "";
}
cat.FlatStyle = FlatStyle.Flat;
cat.Style.BackColor = Color.DarkRed;
continue;
}
//Setting the matched color
cat.FlatStyle = FlatStyle.Flat;
cat.Style.BackColor = Color.SpringGreen;
//Adding the dropdownlist to the size cb
(row.Cells[7] as DataGridViewComboBoxCell).DataSource = dgv.AddSeconderyCombobox(dataGridView1, row.Cells[6].Value.ToString());
if (!matchWithPackage) {
continue;
}
//Matching the size
Stop Point List<string> sizeList = (List<string>)(row.Cells[7] as DataGridViewComboBoxCell).DataSource;
Doesn't reach this and beyond List<string> matchedList = sizeList.FindAll(stringToCheck => stringToCheck.Contains(numberPart));
if (matchedList.Count > 0) {
(row.Cells[7] as DataGridViewComboBoxCell).DataSource = matchedList;
}
}
So i fixt my own problem and i'm going to awnser my own question i case somebody comes across a similar problem.
if (sizeList != null) {
List<string> matchedList = sizeList.FindAll(stringToCheck => stringToCheck.Contains(numberPart));
if (matchedList.Count > 0)
{
(row.Cells[7] as DataGridViewComboBoxCell).DataSource = matchedList;
}
}
After adding the few last lines in an extra check that needed to be done I noticed that the code was being executed, but the matchedlist.count was always 0.
So what was happening is that the code after that was redundant as the matchList was 0 and the debugger just skipped jumping to these lines ( a bit confusing if you ask me ).
I'm swapping my values in List Object on some conditions and update List Object value.
Currently, what I'm doing is
- Looping on each object through List
- Check If condition is net
- Swap values
public static void SwapMinMaxIfNull<T>(this IEnumerable<T> rows, string reportfor)
{
if (reportfor.Equals("Comparison"))
{
var row = rows as IEnumerable<RiskBoardDataToExport>;
try
{
if (rows.Any())
{
var Tests = row.Where(min => min.MinGaitSpeed == null && min.MaxGaitSpeed != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinGaitSpeed = test.MaxGaitSpeed;
test.MaxGaitSpeed = null;
}
}
// again check for next object
Tests = row.Where(min => min.MinTUGTime == null && min.MaxTUGTime != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinTUGTime = test.MaxTUGTime;
test.MaxTUGTime = null;
}
}
// again check for next object
Tests = row.Where(min => min.MinBergScoreSpeed == null && min.MaxBergScoreSpeed != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinBergScoreSpeed = test.MaxBergScoreSpeed;
test.MaxBergScoreSpeed = null;
}
}
//.. for brevity
}
}
}
Can I do it in better way? I know about PropertyInfo i.e. Can check property name and get value etc, but, not having any hint to get this done.
Thanks
It's not exactly what you're asking for, but you can combine the clauses in your Where statements and then have a few if statements in the body:
public static void SwapMinMaxIfNull(this IEnumerable<RiskBoardDataToExport> rows,
string reportfor)
{
if (rows = null) return;
if (reportfor.Equals("Comparison", StringComparison.OrdinalIgnoreCase))
{
foreach (var row in rows.Where(r =>
(r.MinGaitSpeed == null && r.MaxGaitSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null)))
{
if (row.MinGaitSpeed == null)
{
row.MinGaitSpeed = row.MaxGaitSpeed;
row.MaxGaitSpeed = null;
}
if (row.MinTUGTime == null)
{
row.MinTUGTime = row.MaxTUGTime;
row.MaxTUGTime = null;
}
if (row.MinBergScoreSpeed == null)
{
row.MinBergScoreSpeed = row.MaxBergScoreSpeed;
row.MaxBergScoreSpeed = null;
}
}
}
}
As this is an operation where order of the items in the list does not matter, you can easily speed this up by parallelization (you can read up on that here).
So, what you should do, is handle this foreach loop in a parallel way and combine it with Rufus L's optimized code for the fastest result.
var rows = rows.Where(r =>
(r.MinGaitSpeed == null && r.MaxGaitSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null))
Parallel.ForEach(rows, (row) => {
{
if (row.MinGaitSpeed == null)
{
row.MinGaitSpeed = row.MaxGaitSpeed;
row.MaxGaitSpeed = null;
}
if (row.MinTUGTime == null)
{
row.MinTUGTime = row.MaxTUGTime;
row.MaxTUGTime = null;
}
if (row.MinBergScoreSpeed == null)
{
row.MinBergScoreSpeed = row.MaxBergScoreSpeed;
row.MaxBergScoreSpeed = null;
}
}
Note that this requires the System.Threading.Tasks namespace, that's where the Parallel class is.
I am trying to merge duplicate meters and have come op with this method:
private IEnumerable<Meter> MergeDuplicateMeters(IEnumerable<Meter> meters)
{
var tmpMeters = meters.ToList();
var duplicates = tmpMeters
.GroupBy(m => m.MeterUID)
.Where(grp => grp.Count() > 1)
.ToList();
foreach (var duplicate in duplicates)
{
var meter1 = duplicate.FirstOrDefault();
var meter2 = duplicate.LastOrDefault();
tmpMeters.Remove(meter1);
tmpMeters.Remove(meter2);
if (meter2.GsmData != null)
{
if (meter1.GsmData == null)
{
meter1.GsmData = new List<GSMData>();
}
meter1.GsmData.AddRange(meter2.GsmData);
}
if (meter2.P2pMeterSession != null)
{
if (meter1.P2pMeterSession == null)
{
meter1.P2pMeterSession = new List<P2PMeterSession>();
}
meter1.P2pMeterSession.AddRange(meter2.P2pMeterSession);
}
tmpMeters.Add(meter1);
}
return tmpMeters;
}
the problem is that this way I can only merge one time but what if I want to merge every time a new list is read?
I am filtering a collection and I perform 2 filters that are the same but for different fields.
There must be a way I can reduce the duplication of code here?
The checks are whether a date has been entered, and whether it is before a cut off date entered by the user.
public override IList<Company> Search()
{
var list = CacheObjects.Subcontractors;
this.ApplicationFormReturnedCheck(ref list);
this.ApplicationFormSentCheck(ref list);
}
private void ApplicationFormReturnedCheck(ref IList<Subcontractor> list)
{
if (this.ApplicationFormNotReturnedFlag == true && this.ApplicationFormReturned != null)
{
list =
list.Where(x => x.ApplicationFormReturned == null || x.ApplicationFormReturned < this.ApplicationFormReturned).ToList();
}
else if (this.ApplicationFormNotReturnedFlag == true)
{
list = list.Where(x => x.ApplicationFormReturned == null).ToList();
}
else if (this.ApplicationFormReturned != null)
{
list = list.Where(x => x.ApplicationFormReturned < this.ApplicationFormReturned).ToList();
}
}
private void ApplicationFormSentCheck(ref IList<Subcontractor> list)
{
if (this.ApplicationFormNotSentFlag == true && this.ApplicationFormSent != null)
{
list =
list.Where(x => x.ApplicationFormSent == null || x.ApplicationFormSent < this.ApplicationFormSent).ToList();
}
else if (this.ApplicationFormNotSentFlag == true)
{
list = list.Where(x => x.ApplicationFormSent == null).ToList();
}
else if (this.ApplicationFormSent != null)
{
list = list.Where(x => x.ApplicationFormSent < this.ApplicationFormSent).ToList();
}
}
I would suggest you can do something as simple as have some instances of Func<Subcontractor,bool> which cover your various scenarios. This is the type of Func that the Where method expects
To demonstrate let me take one of your methods and show you how:
private void ApplicationFormReturnedCheck(ref IList<Subcontractor> list)
{
var isFormReturned = new Func<Subcontractor,bool>(
x => x.ApplicationFormReturned != null);
var isBeforeDate = new Func<Subcontractor,bool>(
x => x.ApplicationFormReturned < this.ApplicationFormReturned);
var isFormReturnedOrBeforeDate= new Func<Subcontractor,bool>(
x => isFormReturned(x) || isFormReturnedBeforeDate(x));
if (this.ApplicationFormNotReturnedFlag == true && this.ApplicationFormReturned != null)
{
list = list.Where(isFormReturnedOrBeforeDate).ToList();
}
else if (this.ApplicationFormNotReturnedFlag == true)
{
list = list.Where(isFormReturned).ToList();
}
else if (this.ApplicationFormReturned != null)
{
list = list.Where(isBeforeDate).ToList();
}
}
The other method you've shown, although having similar logic, uses a different set of variables. (ApplicationFormSent in place of ApplicationFormReturned). The way I see it you have two options
Duplicate the above within the other method, using the differing variable names
Use a more complex method whereby you have these 3 Func's outside of the scope of each method, and able to distinguish which variables (*Sent or *Returned) to use.
The problem with 2. above is that as your perceved "reuse" goes up, the readability of your code goes down.
In all honesty, I see no major problem with your original code! Its clear, its pretty concise and its easy to see what it's doing. By wrapping all this logic up with predicates (which could conceivably be elsewhere in the class), you're making it harder to read and harder to maintain.
A Func(T, TResult) can be used to encapsulate common predicate methods. In your case, the Func would need to be initilialized in the constructor since you are using instance members in the filter. Ex:
private readonly Func<Subcontractor, bool> _pred;
public Subcontractor()
{
_pred = x => x.ApplicationFormReturned == null || x.ApplicationFormReturned < this.ApplicationFormReturned;
}
private void ApplicationFormReturnedCheck( ref IList<Subcontractor> list )
{
if( this.ApplicationFormNotReturnedFlag == true && this.ApplicationFormReturned != null )
{
list = list.Where( _pred ).ToList();
}
else if( this.ApplicationFormNotReturnedFlag == true )
{
list = list.Where( x => x.ApplicationFormReturned == null ).ToList();
}
else if( this.ApplicationFormReturned != null )
{
list = list.Where( x => x.ApplicationFormReturned < this.ApplicationFormReturned ).ToList();
}
}
I am reading from a list the co-ordinates of a piece of armour. When it finds that specific armour, its supposed to take that armour object, and add it to another list. Yet I receive a null object error (object reference not set to an instance of an object)?
foreach (Armour item in armousOnMap)
{
if (item.Row == _yPosition && item.Column == _xPosition)
{
armourInventory.Add((Armour)item);
}
}
Are all coordinates populated? i.e. not nulls?
var armourInventory = new List<Armour>();
foreach (Armour item in armousOnMap)
{
if ((item.Row != null && item.Row == _yPosition) && (item.Column != null && item.Column == _xPosition))
{
armourInventory.Add((Armour)item);
}
}
You need to initialize list first, and the add items into it.
var armourInventory = new List<Armour>();
Add this line when you are making your list.
var armourInventory = new List<Armour>();
foreach (Armour item in armousOnMap)
{
if (item.Row == _yPosition && item.Column == _xPosition)
{
armourInventory.Add((Armour)item);
}
}