I have a structure
class Node {
List<string> ChildrenIds;
...
}
That I currently store and lookup in RavenDB but Include for results only lets me include down one level in the potentially many leveled tree. Is there any way to tell it to lookup recursively all of these Nodes referenced from the top level?
I know indexes can be used recursively but I wasn't clear on how best to use that to load the correct documents, is it possible to somehow do an Include on an index property?
Yes, you use the JS support in the query, like so:
declare function recursiveInclude(n){
for(var i = 0; i<n.ChildrenIds.length; i++)
recursiveInclude(load(n.ChildrenIds[i]));
return n;
}
from Node as n
select recursiveInclude(n)
Related
Noob here. I've been scouring the internet for days, and cannot find a decent structure that auto-sorts data (like SortedSet), while still allowing that data to be accessible (like List). Here's what I have:
A list containing 100,000 nodes, added and modified regularly.
List<Nodes> nodes;
The node object, containing data I need to access/change
public class Node (string name, int index){ doSomething(); }
I don't wish to be vague, but can't sort the actual list because the index is a history of when nodes were added. Thus, I want to use a structure that auto-sorts KeyValuePair pairs(where string is the name to be sorted by, and int is the index as it is found in my list of nodes), but I must be able to access the value. Here's what I want to do:
// Add a node to the list, then to the structure
int index = nodes.Count;
nodes.Add(new Node("someName", index));
someStructure.Add("someName", index);
// Give name to structure, which returns int value for use in finding node
node[someStructure.findValueOf("someName"))].doSomething();
This would tell the node with the name "someName" to doSomething();
I am positive that I am missing something. I've tried using SortedSet, SortedList, Dictionary, etc. In each case, I can't retrieve the sorted object. What is the purpose of auto-sorting if I can't find out where they are at? Please help my poor life.
You are looking for a SortedDictionary.
As per the documentation: Represents a collection of key/value pairs that are sorted on the key. Although, as some comments say, those 100k objects would be better kept in a database...
Link: https://msdn.microsoft.com/en-us/library/f7fta44c(v=vs.110).aspx
You can use SortedList and LINQ:
SortedList<int,string> list = new SortedList<int, string>();
list.Add(1, "name1");
list.Add(2, "name2");;
var c = list.Select(x => x.Value == "name2").FirstOrDefault();
However I agree with a Christopher's comment about using db.
I need to get info from an XML-file in C#.
Here is some fragments from the XML.
<Surface id="su-62" surfaceType="InteriorWall">
<Name>S-3-7-I-W-62</Name>
<AdjacentSpaceId spaceIdRef="sp-3-TUIN">
</AdjacentSpaceId>
<AdjacentSpaceId spaceIdRef="sp-7-huizen">
</AdjacentSpaceId>
<CADObjectId>Basic Wall: _omgevingsmuur [184610]</CADObjectId>
</Surface>
...
<Surface id="su-63" surfaceType="ExteriorWall">
<Name>N-4-E-W-63</Name>
<AdjacentSpaceId spaceIdRef="sp-4-onthaal">
</AdjacentSpaceId>
<Opening id="su-63-op-1" openingType="NonSlidingDoor">
</Opening>
<CADObjectId>Basic Wall: _detentiemuur [193431]</CADObjectId>
</Surface>
...
<Surface id="su-282" surfaceType="Shade">
<Name>X-S-282</Name>
<CADObjectId>Basic Roof: Generic - 400mm [190976]</CADObjectId>
</Surface>
As you see there are some surfaces that don't have all the info others have.
I have to know which surfaces are adjacent to which space and if there is an opening or not.
(The ultimate goal is to make an 2d array where you can see which SPACE is adjacent to which SPACE and another array to see if the have a joined opening.)
Assuming you have .NET 3.5 or greater, than this is a good use for LINQ2XML.
You can write a query that will grab the associated areas an identify which space are adjacent to each other.
// Load your XML File into an XDocument object
var xDoc = XDocument.Load(xmlPath);
// this is your query, in the end result with my a Dictionary with the Surface
// Id attribute as the key and the AdjacentSpaceId as the value
var result = (from x in xDoc.Descendants("AdjacentSpaceId")
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
}).GroupBy(sp => sp.SurfaceId)
.ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
If you are unfamiliar with any of the LINQ varients, I will attempt to explain the query in a bit more detail. The local field containing your query results is just called results. The var type is another .NET 3.5 addition that tells the compiler to identify the type by the right side of the expression. In this case, result will be a Dictionary<String, IEnumerable<String>>.
In the first line of the query:
from x in xDoc.Descendants("AdjacentSpaceId")
You are basically telling LINQ to iterate through all nodes in the XML called AdjacentSpaceId. It doesn't matter where these nodes are located or what the parent nodes are called. The Descendants() method in LINQ can be very powerful for this reason as it means you don't need to know the exact XML Path to a particular node to select it, you just need the name (see my note at the end)
The next lines of the query are defining exactly what you want to return from the query:
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
})
All LINQ queries using this syntax must have a select statement (there is also a method syntax that can be used instead of or in addition too that doesn't need a select necessarily, but this syntax is easier to learn in my opinion).
In the select clause, you are basically defining a new Anonymous Type that has 3 properties, SurfaceId, SurfaceName, and AdjacentSpace. I'll explain one of the assignments and it should let you understand all of them:
SurfaceId = (String)x.Parent.Attribute("id")
In this line, x refers to the initial line of the LINQ query where you were iterating all of your AdjacentSpaceId nodes, so x is of type XElement.
The Parent property of x is going to select the parent node, in this case Surface. (NOTE: If your root node in your XML happens to be called AdjacentSpaceId, then this line is going to throw an exception since the parent of a root node will be null, but seems to be a safe assumption that it won't be a problem in this case). The Parent property on XElement is also going to be another XElement.
The next part is the Attribute method of XElement, and you are selecting the first attribute of node called id.
So effectively you are selecting the id attribute of all parent nodes to each AdjacentSpaceId nodes.
And lastly, I am casting this to a String. There is a Value property that can be used instead, but my personal preference is to cast to a String because if the Attribute("id") method fails (which could happen if there is no attribute called "id"), calling the Value property will throw an exception, whereas casting to a string will just return null in these cases.
The other parts of this select clause are virtually the same.
The rest of this query are effectively separate queries. I just chained them together, but they could easily be broken out and put on their own.
The next piece:
GroupBy(sp => sp.SurfaceId)
is grouping the result of your LINQ query by the SurfaceId property. This is a key step because it sounds like you want to know where spaces are adjacent to each surface and this will effectively group all of the adjacent spaces together by the surface.
If you aren't familiar, sp => sp.SurfaceId is a Lambda Expression for creating anonymous functions or delegates quickly and easily.
And the final piece will take your grouped results and convert them into something more usable, usually a Dictionary<>, or in this case a Dictionary<String, IEnumerable<String>>
ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
Just one side note about the Descendants() extension method for Linq-to-Xml objects, while it can be very useful as I mentioned above, it can also be very dangerous. When you have multiple nodes in the same XML with the same name but with different purposes or in different parents of the tree, Descendants() will return ALL objects. If you only want to return some of the nodes matching a specific name, you need to filter it down first by using the Element() or Elements() extension methods first to select the proper parent node, then you can safely call Descendants().
I have a object called Category which has an Id, Name, and OwnerId. I then nest these to create Subcategories. If a category has an Owner Id it is a sub category. The number of subcategories is unlimited but each item can only have 1 parent. Simple enough.
My Issue is, I need to access a subcategory after loaded. How do I get the Owning category using Linq. I know the Owner Id but I dont know how many lvls deep the owner could be.
Basically I am looking for a way to get the Category or subcategory where the Id == X but this can live in a subcategory 6 levels or more deep.
I am trying to avoid a loop for each sub category in each sub category....
There is another way to store/retrieve a tree hierarchy as explained in this fogbugz blog post:
Turns out there's a pretty cool
solution for this problem explained by
Joe Celko. Instead of attempting to
maintain a bunch of parent/child
relationships all over your database
-- which would necessitate recursive SQL queries to find all the
descendents of a node -- we mark each
case with a "left" and "right" value
calculated by traversing the tree
depth-first and counting as we go. A
node's "left" value is set whenever it
is first seen during traversal, and
the "right" value is set when walking
back up the tree away from the node.
A picture probably makes more sense:
The Nested Set SQL model lets us add
case hierarchies without sacrificing
performance.
How does this help? Now we just ask
for all the cases with a "left" value
between 2 and 9 to find all of the
descendents of B in one fast, indexed
query. Ancestors of G are found by
asking for nodes with "left" less than
6 (G's own "left") and "right" greater
than 6. Works in all databases.
Greatly increases performance --
particularly when querying large
hierarchies
Here's another post going into more detail. It's written using Sql and php but I think you can get the gist of it and easily translate in Linq to Sql.
In MS SQL 2005 and up you can create recursive queries. In LINQ to SQL however, you are out of luck. Without restructoring the data in the database, there is no way you can traverse the tree in a single database call.
However... there is 1 workaround I can think of. When you are able to group all Category elements of a single tree (or a part of the tree) together, you can pre-load that part of the complete tree in a single statement. After that, you will be able to traverse that part of the tree, without triggering new calls to the database. It would look something like this:
// Load the category that will be used as starting point.
var subCategory = db.Categories.Single(c => c.Id == 56);
// Performance: Load the complete group in one go.
var categories = (
from category in db.Categories
where category.GroupId == subCategory.GroupId
select category)
.ToArray();
// Traverse the tree and get the top-most parent (if any).
var parent = subCategory.GetParents().LastOrDefault();
// Extension method to get the parents.
public static IEnumerable<Category> GetParents(
this Category category)
{
while (category.Parent != null)
{
// NOTE: cat.Parent will not cause a database call
// when the Parent is already loaded by L2S.
yield return cat.Parent;
category = category.Parent;
}
}
This of course will only work if you will be able to determine elements as a group. Whether this solution will be faster also depends on the size of the group. When the group of objects that you load (and don't use) is very big, it will actually slow the application down.
TreeNode[] nodes = this.treeview.Nodes.Find(node.Text, true);
if (nodes.Length > 0)
{
int i = nodes[0].Index;
if (nodes.Length > 0)
this.treeview.Nodes.Remove(nodes[0]);
this.treeview.Nodes.Insert(i, nodes[0]);
}
i tried this code,
but the node nodes[0] is not inserting into the particular index.
instead it is adding at the last.
but yes i use treeviewsorter.
Any idea how to insert node without using insert
or using insert effectively with treeviewsorter??
If you have set the TreeViewNodeSorter property to a custom comparer, your TreeView nodes will automatically be sorted using that comparer.
Thus, you can't insert a node in a different position, because the position is decided using the comparer.
But, in your specific case you're removing a node and inserting it back in its original position, and you say it doesn't work when actually it should do.
This, (I'm guessing) could be due to several reasons:
Your comparer implementation is wrong, or peraphs it uses a property that depends on the sorting itself (like Node.Index)
The node you get with Find() (supposing is just one...), belongs to a level lower than root, but you try to remove it from root nodes and add to that level...
Other reasons, we need more code...
At the moment I am using a custom class derived from HashSet. There's a point in the code when I select items under certain condition:
var c = clusters.Where(x => x.Label != null && x.Label.Equals(someLabel));
It works fine and I get those elements. But is there a way that I could receive an index of that element within the collection to use with ElementAt method, instead of whole objects?
It would look more or less like this:
var c = select element index in collection under certain condition;
int index = c.ElementAt(0); //get first index
clusters.ElementAt(index).RunObjectMthod();
Is manually iterating over the whole collection a better way? I need to add that it's in a bigger loop, so this Where clause is performed multiple times for different someLabel strings.
Edit
What I need this for? clusters is a set of clusters of some documents collection. Documents are grouped into clusters by topics similarity. So one of the last step of the algorithm is to discover label for each cluster. But algorithm is not perfect and sometimes it makes two or more clusters with the same label. What I want to do is simply merge those cluster into big one.
Sets don't generally have indexes. If position is important to you, you should be using a List<T> instead of (or possibly as well as) a set.
Now SortedSet<T> in .NET 4 is slightly different, in that it maintains a sorted value order. However, it still doesn't implement IList<T>, so access by index with ElementAt is going to be slow.
If you could give more details about why you want this functionality, it would help. Your use case isn't really clear at the moment.
In the case where you hold elements in HashSet and sometimes you need to get elements by index, consider using extension method ToList() in such situations. So you use features of HashSet and then you take advantage of indexes.
HashSet<T> hashset = new HashSet<T>();
//the special situation where we need index way of getting elements
List<T> list = hashset.ToList();
//doing our special job, for example mapping the elements to EF entities collection (that was my case)
//we can still operate on hashset for example when we still want to keep uniqueness through the elements
There's no such thing as an index with a hash set. One of the ways that hash sets gain efficincy in some cases is by not having to maintain them.
I also don't see what the advantage is here. If you were to obtain the index, and then use it this would be less efficient than just obtaining the element (obtaining the index would be equally efficient, and then you've an extra operation).
If you want to do several operations on the same object, just hold onto that object.
If you want to do something on several objects, do so on the basis of iterating through them (normal foreach or doing foreach on the results of a Where() etc.). If you want to do something on several objects, and then do something else on those several same objects, and you have to do it in such batches, rather than doing all the operations in the same foreach then store the results of the Where() in a List<T>.
why don't use a dictionary?
Dictionary<string, int> dic = new Dictionary<string, int>();
for (int i = 0; i < 10; i++)
{
dic.Add("value " + i, dic.Count + 1);
}
string find = "value 3";
int position = dic[find];
Console.WriteLine("the position of " + find + " is " + position);
example