Get all the direct Children of a specified Parent - c#

I have a method called GetMenuItems which returns Heirarchical results. Here is the implementation:
public static ObservableCollection<MenuItem> GetMenuItems()
{
XDocument xDoc = XDocument.Load(DirectoryPaths.DataDirectory_General + "MenuItems.xml");
return LoadMenuItems(xDoc.Descendants("MenuItem"));
}
private static ObservableCollection<MenuItem> LoadMenuItems(IEnumerable<XElement> menuItems)
{
return new ObservableCollection<MenuItem>(
menuItems.Select(
x => new MenuItem()
{
Id = Convert.ToDouble(x.Attribute("Id").Value),
Name = x.Element("Name").Value,
ImageData = x.Elements("ImageData").Any() ? x.Element("ImageData").Value : "",
Angle = x.Elements("Angle").Any() ? Convert.ToDouble(x.Element("Angle").Value) : 0,
ScaleX = x.Elements("ScaleX").Any() ? Convert.ToInt32(x.Element("ScaleX").Value) : 0,
ScaleY = x.Elements("ScaleY").Any() ? Convert.ToInt32(x.Element("ScaleY").Value) : 0,
ShortcutKey = x.Elements("ShortcutKey").Any() ? x.Element("ShortcutKey").Value : "",
Background = x.Elements("Background").Any() ? x.Element("Background").Value : "#FF000000",
MouseOver = x.Elements("MouseOver").Any() ? x.Element("MouseOver").Value : "#FF696969",
menuItem = x.Elements("MenuItem").Any() ? LoadMenuItems(x.Elements("MenuItem")) : null
}
)
);
}
Now I want to get all the direct children of a specific Parent.
To make it more clear let me give an example:
Suppose I want to get all the direct children MenuItems of MenuItem whose Id = 1.
Please note that I need to use GetMenuItems() method as I am not allowed to access those XML Files.

You should split your task into two parts:
Finding the menu with a particular ID
Getting its children
The latter is simple - you just use the menuItem property, which would be better named Children or something similar.
I'd tackle the first part by recursion - after making one important change. Instead of the Children property being null if there are no elements, just let it be an empty collection:
Children = LoadMenuItems(x.Elements("MenuItem")
That way, you can check all the children very easily, even if there aren't any - without any nullity checks. Generally, it's easier to represent a lack of items as an empty collection rather than a null reference.
So, to recursively find a menu item by ID, I'd use:
// TODO: Change the ID type from double to almost anything else. double is a
// *terrible* type to use for IDs.
public MenuItem FindMenuItemById(MenuItem item, double id)
{
return item.Id == id ? item : item.Children
.Select(x => FindMenuItemById(x, id))
.Where(found => found != null)
.FirstOrDefault();
}
That will return null if there's no such menu item. Then you can just use:
if (item != null)
{
var children = item.Children;
...
}
As an aside, your other properties can be converted much more simply using the conversion operators on XElement and the null-coalescing operator
// Again, change this! Don't use double!
Id = (double) x.Attribute("Id"),
Name = (string) x.Element("Name"),
ImageData = (string) x.Element("ImageData") ?? "",
Angle = (double?) x.Element("Angle") ?? 0d,
ScaleX = (double?) x.Element("ScaleX") ?? 0d,
ScaleY = (double?) x.Element("ScaleY") ?? 0d,
ShortcutKey = (string) x.Element("ShortcutKey") ?? "",
Background = (string) x.Element("Background") ?? "#FF000000",
MouseOver = (string) x.Element("MouseOver") ?? "#FF696969",

Related

if else if for multiple possible values for a variable C#

I have a if-else if construct where in I am setting a particular value of a variable based on the XName Element local name as follows:
public struct UserObject
{
public string rawValue;
}
var xmlElements= record.Descendants("abc")
.Select(x => x.Elements().ToArray());
foreach (XElement[] elements in xmlElements)
{
foreach (XElement element in elements)
{
UserObject userObject = new UserObject();
if (element.Name.LocalName == "b036") //"b036" is a XML element tag name
userObject.rawValue = (element.Value.Trim()); //note rawvalue is of string type
else if (element.Name.LocalName == "b037")
userObject.rawValue = (element.Value.Trim());
else if (element.Name.LocalName == "b040")
userObject.rawValue = (element.Value.Trim());
}
For the name of the userObject(rawvalue) to load I want to follow this hierarchical order :-
b036, PersonName (e.g., John Smith) if not available use b037
b037, PersonName (e.g., Smith, John) if not available use b040
b040, KeyNames, a.k.a Last Name (e.g., Smith)
If none of b036, b037 or b040 are found, I do not want to set the "rawvalue" for that record
How can I construct my if-else statements or should I use switch case statements to achieve the same?
Since you want to find the value for the highest of certain elements, do a max search. By rolling your own, you avoid having to scan or test twice.
First, create a variable to represent the precedence of the interesting names:
var hierarchy = new[] { "b040", "b037", "b036" };
Now setup some variables to remember the maximum candidate found so far:
XElement candidate = null;
int candidatePos = -1;
Finally, go through all the XMLElement values and remember the maximum found:
foreach (var element in xmlElements.SelectMany(elements => elements)) {
var pos = Array.IndexOf(hierarchy, element.Name.LocalName);
if (pos >= 0) {
if (candidate == null || pos > candidatePos) {
candidate = element;
candidatePos = pos;
}
}
}
If you found a maximum, you can work with it:
if (candidate != null) { //"b036" is a XML element tag name
UserObject userObject = new UserObject();
userObject.rawValue = (candidate.Value.Trim()); //note rawvalue is of string type
// work with userObject
}

Best way to put dash for empty field in C#

I populated a pdf from some fields that are coming from XML in C# code.
I have to put dash (-) for each empty field. Should I check each time if the field is empty put the dash or there is a way to do it for all the fields at once?
What is the best way as I have 50 fields to check.
that is the code I have now:
dt.LastName = (dt.LastName == null ? null : (string)individual.XPathSelectElement("AIndividual[#Type='Co-Applicant']/GivenName/LastName"));
if (dt.LastName == null)
dt.LastName = "-";
I presume dt.LastName originally comes form the same document from another AIndividual element. In this case you could process your document using an array of XPath selectors and property setters. (Mind the code below is a rough sketch and not even compiled):
public class Applicant
{
public string LastName { get; set;}
}
public void Process(XmlElement application, Applicant applicant)
{
var selectors = new[] {
new {
Setter = new Action<Applicant, string>((t,v) => t.LastName = v),
XPath = "GivenName/LastName"
}
};
foreach(var selector in selectors)
{
var node = application.SelectSingleNode("AIndividual[#Type='PrimaryApplicant']/" + selector.XPath) ??
application.SelectSingleNode("AIndividual[#Type='CoApplicant']/" + selector.XPath);
selector.Setter(applicant, node == null ? "-" : node.Value);
}
}

Use a numeric value in a linq dynamic query string

I am trying to make a dynamic linq query that will check for values based on a string.
First of all, here's the query:
objQry = from o in m_Db.OBJECTS.Where(whereConditions)
select o;
if(!objQry.Any())
{
return null;
}
The whereConditions variable is a string I build and pass as parameter to find out the values I need. Here's examples of valid string:
OBJ_NAME == \"Sword\" and OBJ_OWNER == \"Stan\"
This will return any item whose name is "Sword" and owner is "Stan;
OBJ_COLOR == \"Blue\" OR OBJ_COLOR == \"Red\"
This will return any item which color is either blue or red.
Up to there, I'm fine, but now I have a problem: I need to check a decimal field. So I've tried this string:
OBJ_NUMBER == 1
But the query returns null even if there are objects which OBJ_NUMBER value is 1. It's a decimal. How can I indicate the query that they need to check for a decimal value?
**** EDIT ****
I have tried to "modify" the value passed so that it looks like this:
"CARD_NUMBER == Convert.ToDecimal(1)"
And now I have a different kind of error telling me this:
LINQ to Entities does not recognize the method 'System.Decimal ToDecimal(Int32)' method, and this method cannot be translated into a store expression.
Any clues anyone? I'm still looking for a way to do this. Thanks!
EDIT 2
You can get an example of how my code is shaped by looking at this question.
Let's come back at this problem. I want to check decimal values. Let's say that OBJ_NUMBER is a decimal field.
Using Dynamic Linq, I tried to read the decimal field. Say that I want to get each object which number is 1.27. The whereConditions field would then be shaped like this:
OBJ_NUMBER == 1.27
But then I would get an Invalid real literal '1.27' error. I don't know why.
So I have tried Gert Arnold's solution and done this instead:
decimal bDecimal = decimal.Parce(valueToParse);
param = new ObjectParameter("cardNumber", typeof(decimal)) { Value = bDecimal };
valuesToUse.Add("CARD_NUMBER == #cardNumber");
listParams.Add(param);
But I ended up having 2 problems:
The first problem is that my whereConditions string is shaped this way:
CARD_NUMBER == #cardNumber
But I get the following error:
No property or field 'cardNumber' exists in type 'CARD'
Leading me to believe that it cannot make the link between the object parameter and the string used to do the query.
As you can see, I have a list of Params. This is because I cannot know for sure how many parameters the user will chose. So each time the user enters a new search field, I have to create a new ObjectParameter and store it in a list. Here's how I try to do the thing after:
ObjectParameter[] arrayParameters = listParams.ToArray();
// Convert the list to an array
And then, when I try to make the query:
cardQry = from c in mDb.CARD.Where(whereConditions, arrayParameters)
select c;
But to no avail.
RESULTS
Based on the answered question below, I have developped something "awful", yet functional.
First of all, I ignore every decimal fields because I could never reach them with dynamic linq. Instead, I do this:
var valuesToParse = keyValuePair.Value.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
// Here I parse the value and, if that's the case, the symbol.
decimal baseValue = decimal.Parse(valuesToParse[0]);
if (valuesToParse.Count() > 1)
{
string baseMethod = valuesToParse[1];
if (baseMethod == ">" || baseMethod == ">=")
{
if (baseMethod == ">=")
{
baseValue--;
}
// The list is actually like this: Dictionary<string, object> list = new Dictionary<string, object>();
list.Add("low", baseValue);
// I kind of activate a tag telling me that the user is looking for a higher value.
cardHigher = true;
}
else
{
if (baseMethod == "<=")
{
baseValue++;
}
list.Add("low", baseValue);
cardLower = true;
}
}
else
{
//lowParam = new ObjectParameter("dec", typeof(decimal)) { Value = baseValue };
list.Add("low", baseValue);
}
cardNumberActivated = true;
At the end, when I get the list of objects, I do this:
if (list.Count > 0)
{
(example)
if (cardNumberActivated)
{
if (cardHigher)
{
q = mDb.CARD.Where("CARD_NUMBER >= #0", list["low"]).ToList();
}
else if (cardLower)
{
q = mDb.CARD.Where("CARD_NUMBER <= #0", list["low"]).ToList();
}
else
{
q = mDb.CARD.Where("CARD_NUMBER == #0", list["low"]).ToList();
}
}
}
// Here we get the orinalData with the basic filters.
listToReturn.AddRange(cardQry);
if (q != null)
{
//listToReturn.AddRange(q);
for (int i = 0; i < listToReturn.Count; i++)
{
var priceList1 = listToReturn[i];
if (!q.Any(_item => _item.CARD_NUMBER == priceList1.CARD_NUMBER))
{
listToReturn.RemoveAt(i);
i--;
}
}
}
And it works. This is not an elegant way to make it work, but I can validate the fields the way I wanted, and for this, I am thankful at last.
You should not build a query string with inline predicate values. Use parameters in stead. Then will also be able to specify the type:
var whereConditions= "it.CARD_NUMBER = #cardNumber";
var param = new ObjectParameter("cardNumber", typeof(decimal)) { Value = 1 };
objQry = from o in m_Db.OBJECTS.Where(whereConditions, param);
Edit
I don't know what doesn't work in your code. Here's just a random piece of working code derived from one of my own projects:
var param1 = new ObjectParameter("dec", typeof(decimal)) { Value = 90000m };
var param2 = new ObjectParameter("int", typeof(int)) { Value = 90000 };
var q = ValueHolders.Where("it.DecimalValue >= #dec OR it.IntegerValue > #int",
param1, param2).ToList();
Note that param1, param2 could also be an array of ObjectParameter.

selecting an individual element from a list within a list using .where and .select

I have a List<> of type world and within each world element is a List<> of type item which in itself contains a Rectangle and a string
heres the structure of world
`class world
public Items[] items { get; set; }
public world(int nooflevels, int noofitems)
{
//when creating a world make the items
items = new Items[noofitems];
for (int i = 0; i < noofitems; i++)
{
items[i] = new Items();
}
}
}`
and item
class Items
{
public new Rectangle spriterect;
public string type { get; set; }
public Items()
{
spriterect.X = 0;
spriterect.Y = 0;
spriterect.Width = 0;
spriterect.Height = 0;
type = "default";
}
}
this list of worlds was created like this
List<world> Worlds = new List<world>();
i was trying to get a specific rectangle out of the list of items based on the type
void getitem(string thetype, int world)
{
Rectangle a = Worlds[world].
items.Where(f=> f.type == thetype).
Select(g => g.spriterect);
}
so i was hoping this would select the item[].spriterect that contained the .type thetype
and i want it to return the rectangle in that item but it returns an IEnumerable
how do i get it to return the single rectangle based on the type?
You should select single item from items. If there should be single rectangle of specified type, then use SingleOrDefault:
var item = Worlds[world].items.SingleOrDefault(i => i.type == thetype);
if (item != null)
a = item.spriterect;
If you completely sure that there is always exist rectangle of specified type, then simply use Single:
Rectangle a = Worlds[world].items.Single(i => i.type == thetype).spriterect;
You would want to use .Single after you .Select.
Single will throw an Exception if there is not exactly one match.
Rectangle a = Worlds[world]
.items.Where(f=> f.type == thetype)
Select(g => g.spriterect).Single();
Instead of where use FirstOrDefault. If it doesn't find the item, it will return null.
var primary = Worlds[world].FirstOrDefault(f=> f.type == thetype);
if (primary != null)
return primary.spriterect;
return null;
The Where function returns a collection (eveything that meets the criteria), rather than just a single item. You would want to use either First or Single, noting that Single will throw an exception if there is more than one matching the criteria (and both will throw if there are none).
If you only know you will only ever get one value you can use Single, or SingleOrDefault if you know the item may not exist.
//use if you know the rectangle will be there, and there will be only 1 that matches the criteria
Rectangle a = Worlds[world].items.Single(f => f.type == thetype).spriterect;
//use if the rectangle may not be there, and if it is there will be only 1 that matches the criteria
var item = Worlds[world].items.SingleOrDefault(f => f.type == thetype);
if (item != null)
Rectangle a = item.spriterect;

Why I can't remove item from ObservableCollection?

I filled some ObservableCollection<Employe> collection:
// Program.Data.Employees - it is ObservableCollection<Employe>.
Program.Data.Employees.Add(new Employe() { Name="Roman", Patronymic="Petrovich", Surname="Ivanov" });
Program.Data.Employees.Add(new Employe() { Name = "Oleg", Patronymic = "Vladimirovich", Surname = "Trofimov" });
Program.Data.Employees.Add(new Employe() { Name = "Anton", Patronymic = "Igorevich", Surname = "Kuznetcov" });
In other place of my code I try to remove some item from this collection:
// Program.Data.Employees - it is ObservableCollection<Employe>.
Employe x = Program.Data.Employees.First(n => n.Guid == emp.Guid); // x is not null.
Int32 index = Program.Data.Employees.IndexOf(x); // I got -1. Why?
Boolean result = Program.Data.Employees.Remove(x); // I got 'false', and item is not removed. Why?
// But this works fine:
Program.Data.Employees.Clear();
I can clear collection, but I can't remove necessary item. Why it happens?
UPD: Equals method of my Employe class
public bool Equals(Employe other) {
return
other.Guid == this.Guid &&
String.Equals(other.Name, this.Name, StringComparison.CurrentCultureIgnoreCase) &&
String.Equals(other.Patronymic == this.Patronymic, StringComparison.CurrentCultureIgnoreCase) &&
String.Equals(other.Surname == this.Surname, StringComparison.CurrentCultureIgnoreCase) &&
other.Sex == this.Sex &&
String.Equals(other.Post == this.Post, StringComparison.CurrentCultureIgnoreCase);
}
I tried the following code to reproduce your error:
class Employee
{
public string Name { get; set; }
public Guid Guid { get; set; }
}
// ...
ObservableCollection<Employee> employees = new ObservableCollection<Employee>();
var guid1 = Guid.NewGuid();
employees.Add(new Employee { Name = "Roman", Guid = guid1 });
employees.Add(new Employee { Name = "Oleg", Guid = Guid.NewGuid() });
var x = employees.First(e => e.Guid == guid1);
var index = employees.IndexOf(x); // index = 0, as expected
var result = employees.Remove(x); // result = true, as expected
It worked as expected. I would suggest, you set a breakpont at var x = ... and check, if
The collection really contains the item you're looking
If First() really returns that item
Then go to the next line and check, if index is returned correctly. And finally check again, if result is really false.
I see several possible causes of your code failing:
You didn't post the full code and something happens between x=Program.Data.Employees.First() and Program.Data.Employees.IndexOf()
You use multithreaded code (which also results in "something happening" between the two statements). In this case, you need to synchronize the access to the collection
You don't use a ObservableCollection directly but some derived class instead which is constructed by your data layer (such as DataServiceCollection, but this one should work fine too). In this case, check the actual type of your collection in the debugger
Another typical cause of errors with collection would be, if you try to remove items while iterating over the collection (i.e. inside a foreachloop): but in this case an exception should be thrown (and IndexOf should work fine), so this would only apply if you use some derived class which implements non-standard behaviour.
EDIT (in return to you posting your Equal method)
Your Equal method has a serious error in it:
String.Equals(other.Patronymic == this.Patronymic, StringComparison.CurrentCultureIgnoreCase)
... // applies also for following comparisons
should be
String.Equals(other.Patronymic, this.Patronymic, StringComparison.CurrentCultureIgnoreCase)
...
Also, if you're using a Guid, consider only comparing the GUIDs, since this usually means 'unique identifier', so it should be enough to identify some entity.

Categories

Resources