I have a project at my university and I stumbled upon a problem I am not able to solve.
About the program: I need to create a list of tasks(they can be private or business tasks). I need a function that returns a list of ONLY private tasks and another function that returns a list of ONLY business tasks.
So I have a class "Task" that contains "next" and "prev" connections. The classes "PrivateTask" and "BusinessTask" inherit this class. I also have a class ToDoList where I actually try to create the list.
class ToDoList
{
Task first = null;
Task last = null;
//adds new tasks and sorts them right away
public void AddSorted(Task newTask)
{
if(first == null)
{
first = newTask;
last = newTask;
}
else
{
if(newTask < first)
{
Prepend(newTask);
}
else if(newTask > last)
{
Append(newTask);
}
else
{
Task loopTask = first;
while(newTask > loopTask)
{
loopTask = loopTask.next;
}
AddBefore(loopTask, newTask);
}
}
}
//adds a new task before another chosen task
private void AddBefore(Task Next, Task newTask)
{
newTask.prev = Next.prev;
newTask.next = Next;
Next.prev.next = newTask;
Next.prev = newTask;
}
//adds at the start of the list
private void Prepend(Task newTask)
{
first.prev = newTask;
newTask.next = first;
first = newTask;
}
//adds at the end of the list
private void Append(Task newTask)
{
last.next = newTask;
newTask.prev = last;
last = newTask;
}
And now I need to return a list of BusinessTasks
//returns a list of business tasks
public ToDoList GetBusinessList()
{
ToDoList busList = new ToDoList();
Task loopTask = first;
while(loopTask != null)
{
if(loopTask is BusinessTask)
{
busList.AddSorted(loopTask);
}
loopTask = loopTask.next;
}
return busList;
}
But when I return this list the whole content of the main list synchronizes with this one and I cannot understand why.
You aren't putting copies of your tasks into your new list, you are putting references into the new list. As a result, you are changing the same objects. So when you push an item from your first list into the second list and as a result next and/or prev gets changed, you are changing both lists.
So you need to copy the item from your original list and put the new item in the second list.
while(loopTask != null)
{
if(loopTask is BusinessTask)
{
var clone = loopTask.Clone();
busList.AddSorted(clone);
}
loopTask = loopTask.next;
}
Now obviously you'll need to implement a Clone method that will copy all the properties except those that relate to the position in the list (prev and next) to a new instance of BusinessTask
Now if you actually want to have the object in both lists to be references to the same object. So that changing a property on one will change the other, then you can get clever by separating out the data part from the list node part. So you could do something like:
public class TaskBase
{
public string SomeProperty { get; set; }
}
public class Node
{
public TaskBase Data { get; private set;}
public Node Next { get; set; }
public Node Prev { get; set; }
public Node(TaskBase data)
{
Data = data;
}
public Node Clone()
{
// Now all the data part is the same object
// so changing Data.SomeProperty in one list will be
// reflected in both. But the Next and Prev properties
// are independent.
return new Node(Data);
}
}
And then your loop might look like this:
while(loopTask != null)
{
if(loopTask.Data is BusinessTask) // assuming BusinessTask derives from BaseTask
{
var clone = loopTask.Clone();
// clone contains the same BusinessTask, but it's position in the new list
// won't mess up the old list.
busList.AddSorted(clone);
}
loopTask = loopTask.next;
}
Related
I have documents that are based on others, through recursion, I find documents that refer to what they are based on, the problem is that they are all duplicated into one, it does not work correctly
not the correct variant
I want to get this
In the code, the main thing that I transfer is the Type of the document and the List of child elements
public async Task<List<DocumentTreeItem>> FillRecursionTree(Guid documentId, string documentPresentationName, List<DocumentTreeItem> treeItems, Type documentType)
{
await FillRecursionTree(documentId, documentPresentationName, treeItems, documentType);
return treeItems;
async Task FillRecursionTree(Guid childrenId, string childrenIdDocumentPresentationName, List<DocumentTreeItem> childrenIdTreeItems, Type childrenIdDocumentType)
{
var currentNode = new DocumentTreeItem(documentPresentationName, childrenIdDocumentType, childrenIdTreeItems)
{
Id = childrenId,
DocumentPresentationName = childrenIdDocumentPresentationName,
TypeDocument = childrenIdDocumentType
};
childrenIdTreeItems.Add(currentNode);
var customerInvoices = await GetBaseCustomerInvoiceDocuments(childrenId);
foreach (var customerInvoice in customerInvoices)
{
childrenIdDocumentType = customerInvoice.GetType();
await FillRecursionTree(customerInvoice.Id, customerInvoice.DocumentPresentationName, **currentNode.Childrens,** childrenIdDocumentType);
}
}
}
if i think correctly i need to add elements to the root element but how do i do it so i don't get then all the elements again if they don't fit
currentNode.Childrens <-
How can I pass from the main document only the required array of child elements, and not all at once, where everything falls.
Maybe I'm wrong and the problem is in the second one.
This is the type in which I keep everything, there is a children's letter in which all the documents made on its basis are recorded.
public Guid Id { get; set; }
public string? DocumentPresentationName { get; set; }
public Type TypeDocument { get; set; }
public List<DocumentTreeItem> Childrens { get; set; } = new List<DocumentTreeItem>();
public DocumentTreeItem(string documentPresentationName, Type typeDocument, List<DocumentTreeItem> children = null)
{
DocumentPresentationName = documentPresentationName;
TypeDocument = typeDocument;
if (children != null)
Children.AddRange(children);
}
public List<DocumentTreeItem> Children
{
get
{
return Childrens;
}
}
I passed the entire array of elements of the root, and did not create a new one
var currentNode = new DocumentTreeItem(documentPresentationName, childrenIdDocumentType, **new List<DocumentTreeItem>()**)
{
Id = childrenId,
DocumentPresentationName = childrenIdDocumentPresentationName,
TypeDocument = childrenIdDocumentType
};
This is probably a simple task however I am unable to solve.
So currently I have set up a form which contains a textbox and a button and I want to be able to click the button and the first value within the LinkedList will show up in the textbox. If I click the button again then the next value will show up etc.
I currently go it so that the first value will show up but then I am unable to proceed to the next value.
This is the code I have currently:
public class Node
{
public string data;
public Node next;
public Node(string newData)
{
data = newData;
next = null;
}
public void AddEnd(string data)
{
if (next == null)
{
next = new Node(data);
}
else
{
next.AddEnd(data);
}
}
}
public class myList
{
public void AddEnd(string data)
{
if (headnode == null)
{
headnode = new Node(data);
}
else
{
headnode.AddEnd(data);
}
}
public string getFirst() // this gets the first value within the list and returns it
{
if (headnode == null)
{
throw new Exception("List is empty");
}
Node node = headnode;
while (node.next != null)
{
node = node.next;
}
return node.data;
}
I also tried using this:
public class NavigationList<T> : List<T>
{
private int _currentIndex = -1;
public int CurrentIndex
{
get
{
if (_currentIndex == Count)
_currentIndex = 0;
else if (_currentIndex > Count - 1)
_currentIndex = Count - 1;
else if (_currentIndex < 0)
_currentIndex = 0;
return _currentIndex;
}
set { _currentIndex = value; }
}
public T MoveNext
{
get { _currentIndex++; return this[CurrentIndex]; }
}
public T Current
{
get { return this[CurrentIndex]; }
}
}
However, I am not really familiar with something like this so I wasn't sure on how to use it.
So you have a sequence of items, and the only thing that you want, is to get the first item, and once you've got an item, every time your ask for it, you want the next item, until there are no more items left.
In .NET this is called an IEnumerable, or if you know what kind of items are in your sequence, for instance items of MyClass, it is called an IEnumerable<MyClass>. In your case you need an IEnumerable<string>.
Luckily .NET is loaded with classes that implement IEnumerable. Two of the most used ones are array and list. You seldom have to create an enumerable class yourself, re-use the existing ones and enumerate over it.
List<string> myData = ... // fill this list somehow.
IEnumerator<string> myEnumerator = null // we are not enumerating yet.
string GetNextItemToDisplay()
{ // returns null if there are no more items to display
// if we haven't started yet, get the enumerator:
if (this.myEnumerator == null) this.myEnumerator = this.myData.GetEnumerator();
// get the next element (or if we haven't fetched anything yet: get the first element
// for this we use MoveNext. This returns false if there is no next element
while (this.myEnumerator.MoveNext())
{
// There is a next string. It is in Current:
string nextString = enumerator.Current();
return nextString;
}
// if here: no strings left. return null:
return null;
}
This looks like a lot of code, but if you remove the comments it is in fact just a few lines of code:
string GetNextItemToDisplay()
{
if (this.myEnumerator == null) this.myEnumerator = this.myData.GetEnumerator();
while (this.myEnumerator.MoveNext())
return enumerator.Current();
return null;
}
Your ButtonClick event handler:
void OnButtonClick(object sender, eventArgs e)
{
string nextItemToDisplay = this.GetNextItemToDisplay();
if (nextItemToDisplay != null)
this.Display(nextItemToDisplay);
else
this.DisplayNoMoreItems():
}
If you want to start over again with the first element, for instance after changing the List
void RestartEnumeration()
{
this.myEnumerator = null;
}
I have a class which looks like:
private ObservableCollection<string> _areaCollection;
private List<string> _removedAreas;
private List<string> _addedAreas;
public Constructor()
{
_areaCollection = new ObservableCollection<string>();
_areaCollection.CollectionChanged += AreaCollectionChanged;
_removedAreas = new List<string>();
_addedAreas = new List<string>();
}
public IEnumerable<string> Areas { get { return _areaCollection; } }
public IEnumerable<string> AddedAreas
{
get { return _addedAreas; }
}
public IEnumerable<string> RemovedAreas
{
get { return _removedAreas; }
}
public void DisableArea(string areaAlias)
{
if (_areaCollection.Contains(areaAlias))
_areaCollection.Remove(areaAlias);
}
public void EnableArea(string areaAlias)
{
if (!_areaCollection.Contains(areaAlias))
_areaCollection.Add(areaAlias);
}
private void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
var item = e.NewItems.Cast<string>().First();
if (_addedAreas.Contains(item) == false)
{
_addedAreas.Add(item);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
var item = e.OldItems.Cast<string>().First();
if (_removedAreas.Contains(item) == false)
{
_removedAreas.Add(item);
}
}
}
Then in my controller on a Update actionresult I call a method to update the properties:
private bool UpdateProperties(IUser current, IUser update, out IUser result)
{
result = current;
// Update areas
...
return true;
}
So here I want the following result:
_areaCollection of the result contains the updated list
_removedAreas contains the areas which were removed based on the update
_addedAreas contains the areas which were added based on the update
But I am stuck on this.
I believe you are approaching your problem in the wrong way. From what I understand what you want is to know the differences between two collections. For this purpose you do not need ObservableCollection at all, you just need to filter which elements are removed and which are added.
Assuming current and updated both contains a read/write property called Areas:
var updatedSet = new HashSet<string>(updated.Areas);
var currentSet = new HashSet<string>(current.Areas);
var added = updatedSet.Where(a => !currentSet.Contains(a));
var removed = currentSet.Where(a => !updatedSet.Contains(a));
current.Areas = updated.Areas;
If you really need to keep your Areas property read-only you may change the last line with the following code:
foreach(var item in added)
current.EnableArea(item);
foreach(var item in removed)
current.DisableArea(item);
Which is really inefficient.
My question is that is it possible to create a list that sorts the objects in it upon these object being placed in them?
After not getting anywhere, I made a new linked list. The only task is to make this list ordered by the string field of the objects it will containt while remaining foreachable.
I have the following code:
class LancoltLista<T> : IEnumerable
{
class ListaElem
{
public T tartalom;
public ListaElem kovetkezo;
}
ListaElem fej;
public void ElejereBeszuras(T elem)
{
ListaElem uj = new ListaElem();
uj.tartalom = elem;
uj.kovetkezo = fej;
fej = uj;
}
public void VegereBeszuras(T elem)
{
if (fej == null)
{
ElejereBeszuras(elem);
}
else
{
ListaElem e = fej;
while (e.kovetkezo != null)
{
e = e.kovetkezo;
}
ListaElem uj = new ListaElem();
uj.tartalom = elem;
e.kovetkezo = uj;
}
}
public IEnumerator GetEnumerator()
{
return new ListaBejaro(fej);
}
class ListaBejaro : IEnumerator<T>
{
ListaElem elso, jelenlegi;
public ListaBejaro(ListaElem elso)
{
this.elso = elso;
jelenlegi = null;
}
public bool MoveNext()
{
if (jelenlegi == null)
{
jelenlegi = elso;
}
else
{
jelenlegi = jelenlegi.kovetkezo;
}
return jelenlegi != null;
}
public void Reset()
{
jelenlegi = null;
}
object IEnumerator.Current
{
get { return this.jelenlegi.tartalom; }
}
public T Current
{
get { return this.jelenlegi.tartalom; }
}
public void Dispose()
{
elso = null;
jelenlegi = null;
}
}
}
The problem here is that I'm not able to compare p.kulcs and kulcs.
For real world applications you could use the built-in SortedList<T>.
For your homework, you will have to check every item that you get in your add method against the entire list and insert it into the correct place: between the last element that it's grater than or equal to, and the first element that it's smaller then.
Of course, if the list is empty, or if there is no element greater than the one you add, then you simply append the element to the last available location.
Since this is homework, I'll leave you to write the code yourself.
I made a Model Class "RestaurentList" which populates two collections with data from a json file.
After instantiating an object to the class in my ViewModel i databind the collections to an ItemsControl.
Everything in the above works fine, but when i call the method populatePartialList from the object in my ViewModel, it doesn't contain any of the data from when i instantiated the object.
This means, that when my method tries to repopulate the PartialList, it can't because it doesn't find the data from FullList.
Edit: I left out some of the code as you can see with the comment tags.
I just want to give you an understanding of how i am doing this.
My question basicly is, why the object doesn't contain any data when i'm calling the method populatePartialList.
My guess it's got something to do with the fact, that i am databinding the List to an ItemsControl and therefor can't access it anymore? What should do in that case then? I'm trying to make a very simple pagination
Edit to the above; I tried removing my Bind and i still can't reach the data.
Model:
public class RestaurentList
{
private ObservableCollection<Restaurent> _fullList = new ObservableCollection<Restaurent>();
private ObservableCollection<Restaurent> _partialList = new ObservableCollection<Restaurent>();
public ObservableCollection<Restaurent> FullList
{
get { return _fullList; }
}
public ObservableCollection<Restaurent> PartialList
{
get { return _partialList; }
}
public RestaurentList()
{
populateList();
}
public void populatePartialList(int fromValue = 1)
{
int collectionAmount = _fullList.Count;
int itemsToShow = 2;
fromValue = (fromValue > collectionAmount ? 1 : fromValue);
foreach (Restaurent currentRestaurent in _fullList)
{
int currentId = Convert.ToInt32(currentRestaurent.UniqueId);
if (currentId == fromValue || (currentId > fromValue && currentId <= (fromValue + itemsToShow)-1))
{
_partialList.Add(currentRestaurent);
}
}
}
private async void populateList()
{
// Get json data
foreach (JsonValue restaurentValue in jsonArray)
{
// populate full list
foreach (JsonValue menuValue in restaurentObject["Menu"].GetArray())
{
// populate full list
}
this._fullList.Add(restaurent);
}
populatePartialList();
}
public override string ToString()
{
// Code
}
}
View Model:
class ViewModelDefault : INotifyPropertyChanged
{
private RestaurentList _list;
public ObservableCollection<Restaurent> List
{
get { return _list.PartialList; }
}
public ViewModelDefault()
{
_list = new RestaurentList();
_list.populatePartialList(2); // This is where i don't see the data from RestaurentList
}
#region Notify
}
Edit for Jon:
public RestaurentList()
{
PopulatePartialList();
}
public async void PopulatePartialList(int fromValue = 1)
{
await PopulateList();
int collectionAmount = _fullList.Count;
int itemsToShow = 2;
fromValue = (fromValue > collectionAmount ? 1 : fromValue);
foreach (Restaurent currentRestaurent in _fullList)
{
int currentId = Convert.ToInt32(currentRestaurent.UniqueId);
if (currentId == fromValue || (currentId > fromValue && currentId <= (fromValue + itemsToShow)-1))
{
_partialList.Add(currentRestaurent);
}
}
}
private async Task PopulateList()
{
}
Look at the line of code before your call to populatePartialList:
_list = new RestaurentList();
You've created a new instance of RestaurentList. That will have called populateList(), but not waited for it complete. Assuming your real implementation of populateList contains await operations, that means your call to populatePartialList(2) will almost certainly occur before the data is ready.
You need to think about how the asynchrony works here, and how you want it to work. Note that while you can't have an asynchronous constructor, you could have an asynchronous static method... that may well be a better idea for both ViewModelDefault and RestaurentList.