Recursive function issue - c#

I'm working with a tree structure of Installation Places: each one may contain child InstallationPlaces and these can also contain children and so on and so on. I've got the following function:
public JsonResult GetInstPlacesTree()
{
InstallationPlaceModel ipm = new InstallationPlaceModel();
var dataContext = ipm.getRootInstallationPlaces();
var instPlaces = from ip in dataContext.installationPlaces
select new
{
id = ip.installationPlace.id,
Name = ip.installationPlace.mediumDescription,
};
return Json(instPlaces, JsonRequestBehavior.AllowGet);
}
This function returns only the root level of the tree.
I have got two working methods:
one returns the root Installation Places;
the other returns the children of a given Installation Place;
They both return IEnumerable variables.
getRootInstallationPlaces();
getChildInstallationPlaces(id);
How can I achieve to call all the Installation Places and respective children?
I have tried this alternative to the GetInstPlacesTree() function:
private IEnumerable<TreeViewItemModel> GetDefaultInlineData()
{
InstallationPlaceModel ipm = new InstallationPlaceModel();
List<TreeViewItemModel> fullTree = new List<TreeViewItemModel>();
var gipo = ipm.getChildInstallationPlaces(currentInstallationPlace.InstallationPlaceId);
List<TreeViewItemModel> childTree = new List<TreeViewItemModel>();
if (gipo.installationPlaces.Count() > 0)
{
foreach (wsInstallationPlace.installationPlaceOutput child in gipo.installationPlaces)
{
TreeViewItemModel childTreeItem = new TreeViewItemModel
{
Text = child.installationPlace.mediumDescription,
Id = child.installationPlace.id
};
childTree.Add(childTreeItem);
}
}
TreeViewItemModel fatherTreeItem = new TreeViewItemModel
{
Text = currentInstallationPlace.InstallationPlaceMediumDescription,
Id = currentInstallationPlace.InstallationPlaceId,
Items = childTree
};
fullTree.Add(fatherTreeItem);
return fullTree;
}
Any help?

I think something like the following should do what you are after. Essentially it keeps your initial method almost as-is but it populates the child Items of each top-level with a recursive call.
The recursive call grabs the children and adds each child to a List<TreeViewItemModel> to be returned but their children are in turn populated by a call to the recursive function. The recursion will end when there are no children left:
public JsonResult GetInstPlacesTree()
{
InstallationPlaceModel ipm = new InstallationPlaceModel();
var dataContext = ipm.getRootInstallationPlaces();
var instPlaces = from ip in dataContext.installationPlaces
select new TreeViewItemModel
{
id = ip.installationPlace.id,
Name = ip.installationPlace.mediumDescription,
Items = getChildInstallationPlacesRecursive(ip.installationPlace.id, ipm)
};
return Json(instPlaces, JsonRequestBehavior.AllowGet);
}
public List<TreeViewItemModel> getChildInstallationPlacesRecursive(int id, InstallationPlaceModel ipm)
{
List<TreeViewItemModel> children = new List<TreeViewItemModel>();
var gipo = ipm.getChildInstallationPlaces(id);
foreach (wsInstallationPlace.installationPlaceOutput child in gipo.installationPlaces)
{
children.Add(new TreeViewItemModel
{
Text = child.installationPlace.mediumDescription,
Id = child.installationPlace.id,
Items = getChildInstallationPlacesRecursive(child.installationPlace.id, ipm)
});
}
return children;
}

To make it recursive, you have to think that child places are, at the same time, roots of their own childs, then you can call the same function for them.
private IEnumerable<TreeViewItemModel> RecursivePlaces(InstallationPlace root){
var output = new List<TreeViewItemModel>();
output.add(new TreeViewItemModel
{
Text = root.installationPlace.mediumDescription,
Id = root.installationPlace.id
});
foreach(var child in root.installationPlaces)
output.AddRange(RecursivePlaces(child));
return output;
}
//Initial call
RecursivePlaces(ipm.getRootInstallationPlace());

You can solve this without recursion with following approach. I wrote it in some kind of pseudo-code, so you will get an idea what I suggest to accomplish, I didn't used your exact function names and structures and classes...
queue = new List<>();
queue.Add(initialInstallation);
retVal = new List<>();
while (queue.Count > 0) {
retVal.Add(queue[0].GetData());
queue.Add(queue[0].GetChildren());
queue.Remove(0);
}
return retVal;

Related

How to pass a list of strings through webapi and get the results without those strings?

My code already gets the table without containing a string. How can I get a list without containing a list of strings? I want to get the result of SELECT * FROM table WHERE column NOT IN ('x' ,'y');
public IEnumerable<keyart1> Get(string keyword)
{
List<keyart1> keylist;
using (dbEntities5 entities = new dbEntities5())
{
keylist = entities.keyart1.Where(e => e.keyword != keyword).ToList();
var result = keylist.Distinct(new ItemEqualityComparer());
return result;
}
}
I think i found the answer if anybody interested
public IEnumerable<keyart1> Get([FromUri] string[] keyword1)
{
List<keyart1> keylist;
List<IEnumerable<keyart1>> ll;
using (dbEntities5 entities = new dbEntities5())
{
ll = new List<IEnumerable<keyart1>>();
foreach (var item in keyword1)
{
keylist = entities.keyart1.Where(e => e.keyword != item).ToList();
var result = keylist.Distinct(new ItemEqualityComparer());
ll.Add(result);
}
var intersection = ll.Aggregate((p, n) => p.Intersect(n).ToList());
return intersection;
}
}

How can I create a list inside a list with a foreach in c# ?

In other words I need all the elements of list "Categories" to be the "Parent" and elements of list "commodities" be the children.
Example
public string GetCommodities()
{
List<dynamic> categories = new List<dynamic>();
List<dynamic> commodities = new List<dynamic>();
foreach (var comcat in QuickQuoteRepo.CommodityCategories.All().OrderBy(o => o.Order))
{
categories.Add(new
{
comcat.Category,
});
foreach (var com in comcat.Commodities.OrderBy(o => o.Name))
{
commodities.Add(new
{
com.Name,
});
}
}
var response = new JavaScriptSerializer().Serialize(commodities);
return response;
}
And see if it's possible to all commodities names inside each category, within this foreach.
I tried adding a dynamic list such as:
dynamic listOfElements = new { CAT = categories, COMM = commodities };
But it does't return elemnts as parents or dependency of categories. Is the same as adding
commodities.Add(new
{
comcat.Category,
com.Name,
});
public string GetCommodities()
{
List<dynamic> categoryCommodityList = new List<dynamic>();
foreach (var comcat in QuickQuoteRepo.CommodityCategories.All().OrderBy(o => o.Order))
{
var allCommodities = comcat.Commodities.OrderBy(o => o.Name).Select(com => com.Name).ToList();
categoryCommodityList.Add(new { Catagory = comcat.Category, Items = allCommodities } );
}
return new JavaScriptSerializer().Serialize(categoryCommodityList);
}
You class structure does not support parent-child relationships. I mean, if what you want is that each Category holds a list of commodities, then you would need something like this:
var result = from c in comcat
select new { Category = c, Commoddities = c.Commoddities};
This will return a hierarchy of Categories including all Commodities underneath it.
If you are just receiving a flat data set, then you need something like this:
var result = from c in comcat
select new { Category = c,
Commoddities = c.Where(x=>x.Category.Name == c.Name).Select(x=>x.Commodity) };
Hopefully you get the idea...

Parallel task on tree structure not complete

Very new to .NET task parallelism. The objective is walking through a tree structure, where each branch is composed of one parent node, one child node and one operation node(like a weight). And for each node, create an extension object and save it to db. I followed a possible duplicate conversation. But the observation is that the tree is not walked through completely. The process would exit early unexpectedly. Following is my code:
public void InitializeScheduleVariables_Parallel(IResource ANode, double aNumRequired, double aBatchRequired, double aAcceptProbability, AppContext aAppContext, bool ARecursively = true)
{
var LTasks = new List<Task>();
var LUser = aAppContext.LocalContext.User;
LTasks.Add(Task.Factory.StartNew(() =>
{
var LNewContext = new AppContext(new DbContext(new Context(LUser)));
var LNewRep = new ResourceRepository(LNewContext);
ANode = LNewRep.Get(ANode.Id);
ANode.ResourceInstance_Create(); // Create the ResourceInstance on the Resourse if it not already exists.
ANode.ResourceInstance.Required = aNumRequired;
ANode.ResourceInstance.ScheduleSource = ResourceInstance.ScheduleSourceEnum.Undefined;
ANode.ResourceInstance.ScheduleState = ResourceInstance.ScheduleStateEnum.Unscheduled;
ANode.ResourceInstance.ScheduleMode = ResourceInstance.ScheduleModeEnum.Undefined;
ANode.ResourceInstance.BatchRequired = aBatchRequired;
ANode.ResourceInstance.ProbabilityOfCompletion = aAcceptProbability;
ANode.ResourceInstance.Save();
}));
if (ARecursively)
{
foreach (AssemblyLink LAssembly in ANode.GetOutEdges())
{
LTasks.Add(Task.Factory.StartNew(() =>
{
// SET The Variables for the Production Operations AS WELL
IOperationResource LOperation = LAssembly.Operation;
if (LOperation != null)
{
var LNewContext = new AppContext(new DbContext(new Context(LUser)));
var LNewRep = new OperationResourceRepository(LNewContext);
LOperation = LNewRep.Get(LOperation.Id);
LOperation.ResourceInstance_Create(); // Create the ResourceInstance on the Resourse if it not already exists.
LOperation.ResourceInstance.Required = aNumRequired / LAssembly.OutputQuantity;
LOperation.ResourceInstance.BatchRequired = aBatchRequired / LAssembly.OutputQuantity;
LOperation.ResourceInstance.ScheduleSource = ResourceInstance.ScheduleSourceEnum.Undefined;
LOperation.ResourceInstance.ScheduleState = ResourceInstance.ScheduleStateEnum.Unscheduled;
LOperation.ResourceInstance.ScheduleMode = ResourceInstance.ScheduleModeEnum.Undefined;
LOperation.ResourceInstance.ProbabilityOfCompletion = aAcceptProbability;
LOperation.ResourceInstance.Save();
}
}));
LTasks.Add(Task.Factory.StartNew(() =>
{
// Recursively SET Child NODES
IResource LChildNode = LAssembly.Child;
double LNumRequired_Child = aNumRequired * LAssembly.InputQuantity / LAssembly.OutputQuantity;
double LNumBatchRequired_Child = LChildNode.Quantity * LAssembly.InputQuantity / LAssembly.OutputQuantity;
InitializeScheduleVariables_Parallel(LChildNode, LNumRequired_Child, LNumBatchRequired_Child, aAcceptProbability, aAppContext, ARecursively);
}));
}
}
Task.WaitAll(LTasks.ToArray());
}
Could anyone share some thought? Thank you.

ArgumentOutOfRangeException when writing to a model

I'm trying to write to a model for the first time to use in my view: the first time I write to the model I get an ArgumentOutOfRangeException.
Getting error on first write to array:
private IAdditionalQuestionsService _service;
private SelectedAdditionalQuestionAnswerModel _model;
private void InitializeController()
{
_service = GetObject<IAdditionalQuestionsService>();
//GetPageHeaderText(inst);
ViewBag.GetPageTitle = "Additional Questions";
}
[HttpGet]
public virtual ActionResult Edit()
{
Institution inst = _service.GetInstitution(State.GetInstitutionRecordId());
_model = GetObject<SelectedAdditionalQuestionAnswerModel>();
_model.AddQuestAnswModel = new List<AdditionalQuestionAnswerModel>();
GetPageConfiguration1(inst);
return View(_model);
}
AdditionalQuestionAnswerModel m = GetObject<AdditionalQuestionAnswerModel>();
int c = 0;
foreach (var x in inst.AdditionalQuestions)
{
foreach (var y in x.AdditionalQuestionAnswers)
{
// Error is happening on next line *************
_model.AddQuestAnswModel[c].QuestionText = x.QuestionText;
_model.AddQuestAnswModel[c].InstitutionId = x.InstitutionId;
_model.AddQuestAnswModel[c].AdditionalQuestionId = x.Id;
_model.AddQuestAnswModel[c].AnswerText = y.AnswerText;
_model.AddQuestAnswModel[c].IsSelected = false;
c++;
}
}
You can't use _model.AddQuestAnswModel[c] because you never added any items to your list.
Instead of that, create a new object and set its values and then add the item to your list.
Something like this:
AdditionalQuestionAnswerModel newItem = new AdditionalQuestionAnswerModel();
//set the values here to newItem
_model.AddQuestAnswModel.Add(newItem);
You're firstly instantiating your list
_model.AddQuestAnswModel = new List<AdditionalQuestionAnswerModel>();
then you try to access to the first element
_model.AddQuestAnswModel[c] // c == 0
without adding any element to the list.
Add an element before trying to access to a list by index, or more simple:
foreach (var y in x.AdditionalQuestionAnswers)
{
AdditionalQuestionAnswerModel newObj = new AdditionalQuestionAnswerModel
{
QuestionText = x.QuestionText;
InstitutionId = x.InstitutionId;
AdditionalQuestionId = x.Id;
AnswerText = y.AnswerText;
IsSelected = false;
};
_model.AddQuestAnswModel.Add(newObj);
}
Ir means that there no item in your _model.AddQuestAnswModel at the indicated postition, and from your code, I see that _model.AddQuestAnswModel has only be initiated with new List<AdditionalQuestionAnswerModel>(), so it does not contain items (unless you're doing it in the contructor).
You need to fill it like so :
_model.AddQuestAnswModel.Add(item);

Adding properties of an Object together in a collection

I have an app that creates ContactList Objects and adds them to a Dictionary collection. My ContactList objects have a property called AggLabels which is a collection of AggregatedLabel objects containg Name and Count properties. What I am trying to do is change the "else" case of my code snippet so that before adding a new AggregatedLabel it will check whether the AggLabel.Name exists in the AggregatedLabel collection and if this is true it will not add the AggLabel.Name again. Instead it will add the value of AggLabel.Count (type int) to the existing AggregatedLabel object. So for an existing object, if the first Count value was 3 and the second value is 2 then the new Count value should be 5. In simple terms I want to have unique AggLabel Names and add together the Counts where the Names are the same. Hope that makes sense - would appreciate any help. Thanks!
Code snippet
Dictionary<int, ContactList> myContactDictionary = new Dictionary<int, ContactList>();
using (DB2DataReader dr = command.ExecuteReader())
{
while (dr.Read())
{
int id = Convert.ToInt32(dr["CONTACT_LIST_ID"]);
if (!myContactDictionary.ContainsKey(id))
{
ContactList contactList = new ContactList();
contactList.ContactListID = id;
contactList.ContactListName = dr["CONTACT_LIST_NAME"].ToString();
//contactList.AggLabels = new ObservableCollection<AggregatedLabel>() { new AggregatedLabel() { Name = dr["LABEL_NAME"].ToString(), Count = Convert.ToInt32(dr["LABEL_COUNT"])}};
contactList.AggLabels = new ObservableCollection<AggregatedLabel>()
{
new AggregatedLabel()
{
Name = dr["LABEL_NAME"].ToString(),
Count = Convert.ToInt32(dr["LABEL_COUNT"])
}
};
myContactDictionary.Add(id, contactList);
}
else
{
ContactList contactList = myContactDictionary[id];
contactList.AggLabels.Add(
new AggregatedLabel()
{
Name = dr["LABEL_NAME"].ToString(),
Count = Convert.ToInt32(dr["LABEL_COUNT"])
}
);
}
}
}
There are two possible solutions I can think of:
1) Use a dictionary instead of the collection of aggregated labels the same way you do it for the contact dictionary. When yout use the name as key and the count as value, you can use the ContainsKey-Method to check whether the label already exists.
contactList.AggLabels = new Dictionary<string, int>();
...
else
{
ContactList contactList = myContactDictionary[id];
if (contactList.AggLabels.ContainsKey(dr["LABEL_NAME"].ToString()))
{
contactList.AggLabels[dr["LABEL_NAME"].ToString()] += Convert.ToInt32(dr["LABEL_COUNT"]);
}
else
{
contactList.AggLabels.Add(dr["LABEL_NAME"].ToString(), Convert.ToInt32(dr["LABEL_COUNT"]));
}
}
2) I you need to use the AggreagteLabel object you can use a loop to search throug all labels.
else
{
bool flagAggLabelFound = false;
ContactList contactList = myContactDictionary[id];
foreach(AggregateLabel aggLabel in contactList.AggLabels)
{
if(aggLabel.Name == dr["LABEL_NAME"].ToString())
{
aggLabel.Count += Convert.ToInt32(dr["LABEL_COUNT"]);
flagAggLabelFound = true;
break;
}
}
if (!flagAggLabelFound)
{
contactList.AggLabels.Add(
new AggregatedLabel()
{
Name = dr["LABEL_NAME"].ToString(),
Count = Convert.ToInt32(dr["LABEL_COUNT"])
}
);
}
}
I hope this helps.
I would try this:
ContactList contactList = myContactDictionary[id];
AggregateLabel existing = contactList.AggLabels.FirstOrDefault(
l => l.Name == dr["LABEL_NAME"].ToString()
);
if (existing == null) { contactList.AggLabels.Add(
new AggregatedLabel() {
Name = dr["LABEL_NAME"].ToString(),
Count = Convert.ToInt32(dr["LABEL_COUNT"])
}
);
}
else { existing.Count += Convert.ToInt32(dr["LABEL_COUNT"]); }
#extract these Aggregated Labels and put them in a separate Observable collection:
1) If you a Dictionary for storing the labels in the contact list, this should work:
ObservableCollection<AggregateLabel> copyOfAggregateLabels = new ObservableCollection<AggregateLabel>();
foreach (KeyValuePair<string, int> aggLabel in aggregateLabels)
{
copyOfAggregateLabels.Add(
new AggregatedLabel() {
Name = aggLabel.Key,
Count = aggLabel.Value
}
);
}
2) If you use an ObservableCollection of AggregateLabels, you get an AggregateLable instead of a KeyValuePair in the loop. The rest works the same way.
First I thought of something like:
ObservableCollection<AggregateLabel> copyOfAggregateLabels = new ObservableCollection<AggregateLabel>(aggregateLables);
But this way you get a new ObservableCollection, but the labels stored in the new collection are still referring to the same objects as the ones in the collection you copy.

Categories

Resources