I have a custom object of School, which is part of a family of schools. So when I return a school, I can return the schools it is linked to in its family.
However, when I do this it manages to loop forever, how can I stop it. Like only going 1 level deep and not becoming recursive?
public class School
{
public long BaseId { get; set; }
public string BaseName { get; set; }
public string SchoolFamily { get; set; }
public List<School> LinkedSchools
{
get
{
var schoolRepository = new SchoolRepository();
return schoolRepository.GetAllSchoolsLinkedByFamily(SchoolFamily).ToList();
}
set { ; }
}
}
Add a Boolean member variable to act as a flag, default cleared. First thing to do in the get function is check that flag. If it is set, return an empty list. Otherwise set the flag, create the list, and clear the flag, then return the list.
You can pass the current instance of your School (this) into the GetAllSchoolsLinkedByFamily and when that reaches the instance you can stop.
Change the LinkedSchools property to be a method that accepts an optional integer parameter of nestLevel. Something like this:
public List<School> GetLinkedSchools(int nestLevel)
{
// Get schools logic here...
}
Then change your code so that each level of recursion increments a counter and returns once the counter is equal to nestLevel.
I would recommend first adding the current school to the return Collection and passing that Collection through each recursive iteration. Then, inside the iteration method, only add / perform a deeper iteration for the schools that are not already in the list. In this way you get all of the related schools but no endless recursion.
Edit: Mark Jones Graph Traversal (marking visited nodes) suggestion below is actually cheaper if you don't mind adorning your class.
Related
I have a variable [User::WorkOrderProductIdList] in SSIS package containing records of a class object.
Work Order Product class
public class WorkOrderProduct
{
public Guid workOrderId;
public Guid workOrderProductId;
public static Guid WorkOrderId { get; set; }
public static Guid WorkOrderProductId { get; set; }
}
Main Script Task
public override void InputWOProduct_ProcessInputRow(InputWOProductBuffer Row)
{
ArrayList wopList = new ArrayList();
WorkOrderProduct wop = new WorkOrderProduct();
wop.workOrderId = pWorkOrderID;
wop.workOrderProductId = pWorkOrderProductID;
wopList.Add(wop);
}
Assign wopList to [User::WorkOrderProductIdList]
public override void PostExecute()
{
base.PostExecute();
Variables.WorkOrderProductIdList = this.wopList;
}
In another script task, it takes in [User::WorkOrderProductIdList] as ReadOnlyVariables.
May I know how can I loop through [User::WorkOrderProductIdList] and extract the values of workOrderId and workOrderProductId for each row?
I saw that my ArrayList [User::WorkOrderProductIdList] contains the records and values, but there are no functions when . on the field.
Population
Intellisense issue aside, you'll only ever have at most 1 row in there
You are using a Data Flow and within that, you have a Script Component acting as a Transformation.
In InputWOProduct_ProcessInputRow which fires for each row that passes through the component, you have
Every time a new row comes in, you are going to empty out the existing ArrayList and reinitialize it.
Instead, you need to have that variable at the class scope and have the initialization logic in the not-shown PreExecute method
ArrayList wopList;
// Or, if you wish to use the Generics
// List<WorkOrderProduct> wopList
public override void PreExecute()
{
base.PreExecute();
/*
* Add your code here
*/
this.wopList = new ArrayList();
// Or
// this.wopList = new List<WorkOrderProduct>();
}
Consumption
You use the ArrayList to hold the elements of your array but that is a weakly typed list.
We don't recommend that you use the ArrayList class for new development. Instead, we recommend that you use the generic List class.
When you're enumerating through it in your foreach loop, what is getting popped off the list is of type Object. Not only do I just "know" that, itellisense is telling you that all it knows is the type is Object because it's giving you the functions that everything has because they're all derived from Object.
Yes, the Watch window has inspection magic to show you what the values are but do you think the team that wrote the former is the same team that wrote the latter?
Since you "know" what the type should be, declare it as such.
foreach (WorkOrderProduct wopObj in ...
However, the next logical error, probably, is going to be in the accessing of
Variables.WorkOrderProductIdList itself. Your snipped image there shows you're shredding out the array in the PreExecute method. The sequence of operations is that the Data Flow is going to go through validation, then pre-execute sequences so at that point, it's going to shred the results of your array list and the value of wopObj_workOrderId is going to be the last element of your array.
Assuming I have an object of some type, containing an array of some other type:
public class Mammal
{
public Leg[] Legs { get; set; }
}
public class Leg
{
public int Length { get; }
}
If I e.g. receive a Mammal from some GetMammals() call, how would I go about filtering Legs in Mammal based on a predicate, such as height for example?
Mammal.Legs.Where(l => l.Length > 10);
Above would return an IEnumerable<Legs> which I would then have to find a way to stuff back into Mammal, but is there a neat way to perform this action directly on my Mammal object, assuming I would have to use this filteredMammal object for something else later on?
You allready have written the solution yourself. Every linq-method just returns a new collection instead of modifying the existing one.
In order to do so you´d have to set the returned collection to your mammals property:
myMammal.Legs = myMammals.Legs.Where(myPredicate).ToArray();
This assumes you can set the property.
Another opportunitiy is to write a Remove-method:
void Remove(Predicate<Leg> predicate)
{
this.Legs = this.Legs.Where(x => !predicate(x)).ToArray();
}
This still requires a private setter though.
The best approach is to make Legs a List<Leg>. Then you can just call RemoveAll:
myMammal.Legs.RemoveAll(myPredicate);
Here is my situation. I have 2 list of the same type. Imagine the names like these. FullList and ElementsRemoved. So in order to avoid the database roundtrip, anytime I delete an element from the fulllist I added to the list of ElementsRemoved in case of regret's user so he can revert the deletion.
I was thinking to loop inside my ElementsRemoved to insert them again into the FullList from where initially were removed.
There is any way to do this as simple with List Methods.
Something like
FullList.Insert, Add, ..... (x =>
in order to reduce line code and optimized?
Instead of deleting the item from your database consider using a flag in the table.
For example consider this entities table (written in TSQL):
CREATE TABLE Entity
(
Id INT IDENTITY PRIMARY KEY
,Name NVARCHAR(20) NOT NULL
,IsDelete BIT NOT NULL DEFAULT 0
);
This way you can set the IsDelete bit when the user deletes the entity which will prevent the data from being lost. The data can be pruned on a job in the off hours.
The would lead to only needing one list instead of keeping track of two lists.
public class Entity
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsDelete { get; set; }
}
public static void UndoDelete(IEnumerable<Entity> fullList, int[] removedIds)
{
foreach(var entity in fullList.Where(e => removedIds.Contains(e.Id)))
{
entity.IsDelete = false;
}
}
In case you cannot modify your application.
You can simply add the entities back in.
See List(T).AddRange
var entitiesToAdd = new[] { 2, 3, 4 };
var entitiesToInsert = ElementsRemoved.Where(e => entitiesToAdd.Contains(e.Id));
FullList.AddRange(entitiesToInsert);
In your front end make a class that holds a bool and your object:
public class DelPair<T>{
public bool IsDeleted{get;set;}
public T Item{get;set;}
}
Now instead of using a list of objects use a list of DelPair<YourClass> and set IsDeleted=true when deleting.
This pattern will also allow you to track other things, such as IsModified if it comes to that.
Based on OP comment that he's using an ENTITY class and needs it to function as such:
One option is to make your DelPair class inherit ENTITY. Another may be to put implicit casting operator:
...
// not exactly sure about the signature, trial/error should do :)
public static implicit operator T(DelPair<T> pair)
{
return pair.Item;
}
Suppose you have an element having a field id which uniquely identifies it.
class Element{public int id;}
In that case you can do this
FullList.Add(ElementsRemoved.FirstOrDefault(e=>e.id==id));
In case you want to add all elements use AddRange
FullList.AddRange(ElementsRemoved);
You can use the AddRange method
FullList.AddRange(ElementsRemoved);
But consider doing this
public class YourClass
{
public string AnyValue{get;set;}
public bool IsDeleted{get;set;}
}
And you have list like this List < YourClass> FullList. Now whenever user removes any item you just set the
IsDeleted = true
of the item that is removed. This will help you in keeping just one list and adding removing from the list
I'm trying to use a lambda expression to remove a certain object from a list, based on a value within that object. Here is my lambda:
ChartAttributes.ToList().RemoveAll(a => a.AttributeValue.Contains("PILOT"));
Here is the ChartAttributes list
IList<IChartAttribute> ChartAttributes
Here is the object ChartAttribute contained within the above list
public virtual string AttributeKey { get; set; }
public virtual string AttributeValue { get; set; }
public virtual int ChartAttributeId { get; set; }
public virtual int ChartSpecificationId { get; set; }
There is a chart attribute with its AttributeKey set to "PILOT". But this never gets removed. What am I doing wrong?
Thanks
Your code is taking an IEnumerable, copying all of its elements into a list and then removing items from that copy. The source IEnumerable is not modified.
Try this:
var list = ChartAttributes.ToList();
list.RemoveAll(a => a.AttributeValue.Contains("PILOT"));
ChartAttributes = list;
EDIT
Actually a better way, without needing to call ToList:
ChartAttributes = ChartAttributes.Where(a => !a.AttributeValue.Contains("PILOT"));
Your call to .ToList() makes a new list, and you end up removing the item from that list.
Whatever ChartAttributes is, you're not touching the contents of that.
Basically you're doing this:
var newList = ChartAttributes.ToList();
newList.RemoveAll(...);
If you were to inspect the contents of newList at this point you'd notice that your object(s) had been removed, but ChartAttributes, whatever type that is, still has those objects present.
You will have to remove the objects directly from ChartAttributes, but since you didn't say which type that is, I can't give you an example of how to do that.
If you need to remove items and save to database, you can try this sample code:
foreach (var x in db.myEntities.Where(a => a.AttributeValue.Contains("PILOT")))
db.myEntities.Remove(x);
db.SaveChanges();
It doesn't use RemoveAll, but it's another option to save the contents.
I had a similar problem and did a cast instead (as my setter for the property was internal):
((List<IChartAttribute>)ChartAttributes).RemoveAll(a => a.AttributeValue.Contains("PILOT"));
Scenario:
i have a web form from where i m taking input for Item class now i want to assign values to feature that have return type of list how can i do that.
item value = new item(),
value.feature = serialtextbox.text; //error
foreach ( var item in value) //error
{
item.SerialNo= serialtextbox.text;
}
Item and Item feature classes
Class Item
{
list<Itemfeature> features;
}
class ItemFeature
{
public int SerialNo
{
get { return serialno; }
set { serialno = value; }
}
public int Weight
{
get { return weight; }
set { weight = value; }
}
}
Plz help me out
Note: No language is specified, but it looks like C#. I'm assuming C# in this answer.
It's not really clear what you're trying to do here, but I'll give it a shot. First of all, you're going to want to post the actual code you're using. This code won't even compile, it's loaded with syntax errors.
Let's take a look at your objects first:
class Item
{
List<ItemFeature> features;
}
class ItemFeature
{
public int SerialNo
{
get { return serialno; }
set { serialno = value; }
}
public int Weight
{
get { return weight; }
set { weight = value; }
}
}
You have a custom class, ItemFeature, which consists of a serial number (integer) and a weight (integer). You then have another custom class, Item, which consists of a list of ItemFeatures.
Now it looks like you're trying to add a new ItemFeature to the Item and then loop through all of them and set them again?. Something like this, perhaps?:
Item value = new Item();
value.features.Add(new ItemFeature { SerialNo = int.Parse(serialtextbox.Text) } );
foreach (var item in value.features)
{
item.SerialNo = int.Parse(serialtextbox.Text);
}
(Note that this code is probably as free-hand as your code, so I haven't tested it or anything.)
What I've changed here is:
Setting the SerialNo property, rather than trying to set the ItemFeature directly to a value. You need to dig into the object's property to set a value on that property, not just set it to the entire object.
Converting the input (a string) into the property's type (an int).
Looping through the list, not the Item object itself. The Item object contains a list as a property, but the object itself isn't a list. You can loop through the property, not through the parent object.
A few things to ask/note:
What exactly are you trying to do? You have a list of objects, but you're only setting one and then looping through that one to set it again. Why?
You may want to consider more apt class/property names. Things like "Item" can be a bit unclear.
Your Item class has a public variable, features. This is generally frowned upon. It's better to use a property. That way if you ever have to add logic behind it you won't break compatibility outside of the object itself. The ItemFeature class has properties like this, which is good. They can be additionally shortened by using automatic properties if you'd like, just to keep things clean and simple.
Note that my code isn't doing any input checking on the serialtextbox.Text value. It should be. I presented it in a simpler form as an introductory approach to something that will work under ideal conditions. But something like the following would be better:
var serialValue = 0;
if (!int.TryParse(serialtextbox.Text, out serialValue))
{
// Here you would probably present an error to the user stating that the form field failed validation.
// Maybe even throw an exception? Depends on how you handle errors.
// Mainly, exit the logic flow.
return;
}
var value = new Item();
value.features.Add(new ItemFeature { SerialNo = serialValue } );
Edit: I just noticed that my call to .Add() will actually fail. You'll want to initialize the list before trying to use it. Consider changing the Item class to something like this:
class Item
{
public List<ItemFeature> features { get; set; }
public Item()
{
features = new List<ItemFeature>();
}
}
Two things changed here:
I converted the public member to a property, as previously mentioned.
I added a constructor which initializes the list so that it can be used. Otherwise, being a reference type, it would default to null. So any call to .Add() or any other method on the list would throw a NullReferenceException because there's no object on which to call the method(s).