代码之家  ›  专栏  ›  技术社区  ›  Burt

LINQ查询难题

  •  1
  • Burt  · 技术社区  · 14 年前

    如果我有一个具有父子关系的对象,其中ID和parentID表示记录的唯一ID和记录的父ID。

    我需要一个查询,它将查询一个对象列表,并将路径返回到每个项的关系根(即,路径=(alltheparentnames)\name)。

    如有任何帮助,我们将不胜感激。

    我的对象

    国际身份证
    内景?父ID
    字符串名称
    字符串路径

    3 回复  |  直到 14 年前
        1
  •  1
  •   Steck    14 年前

    这是预期结果吗?

    class A
    {
        public int Id;
        public int? ParentId;
        public string Name;
    
    
        public static Func<T1, T2> Fix<T1, T2>(Func<Func<T1, T2>, Func<T1, T2>> f)
        {
            return f(x => Fix(f)(x));
        }
    
        public static string[] GetPaths(A[] array)
        {
            return array.Select(
                Fix<A, string>(self => x => x.ParentId != null ? self(array.First(a => a.Id == x.ParentId.Value)) + "\\" + x.Name : x.Name)
                ).ToArray();
        }
    }
    

    在没有递归的单个查询中是不可能的(foreach(aggregate)on array可能很适合模拟递归,但它很愚蠢)

    修复-是 Fixed point combinator

        2
  •  4
  •   RameshVel    14 年前

    我相信这会给你预期的结果

            Func<int?, string> GetParents = null;
    
            List<URClass> lstRoot = new List<URClass>();
            lstRoot.Add(new URClass() { Id = 1, Name = "a", ParentId = null, Path = "1" });
            lstRoot.Add(new URClass() { Id = 2, Name = "b", ParentId = 1, Path = "1" });
            lstRoot.Add(new URClass() { Id = 3, Name = "c", ParentId = 2, Path = "1" });
            lstRoot.Add(new URClass() { Id = 4, Name = "d", ParentId = 3, Path = "1" });
            lstRoot.Add(new URClass() { Id = 5, Name = "e", ParentId = 4, Path = "1" });
    
            GetParents = i =>
            {
                var str = string.Empty;
                var outt = lstRoot.Where(x => x.Id == i).Select(x=>new {x.Name,x.ParentId });
    
                foreach (var lst in outt)
                {
                    str += lst.Name;
                    if (lst.ParentId != null)
                    {
                          var outts = GetParents(lst.ParentId);
                          str += "," + outts;
                    }                    
                }
                return str;
    
            };          
    
            var ks = from p in lstRoot
                     join q in lstRoot on p.Id equals q.ParentId
                     select new { q.Id, parentName = p.Name, parentid=p.Id, gpid=p.ParentId };
    
    
            List<string> RelationShip = new List<string>();
    
            foreach (var lst in ks)
            {
                var str = lst.parentName;
                if (lst.gpid != null)
                {
                     var prnt = GetParents(lst.gpid);
                     if (prnt != null)
                        str += "," + prnt;                   
                }
                str += "/" + lst.Id;
                RelationShip.Add(str); 
            }
    
    
            foreach (var ret in RelationShip)
                Console.WriteLine(ret);
    

    输出将是

    A/2

    B,A/3

    C,B,A/4

    D,C,B,A/5

        3
  •  2
  •   Eric Lippert    14 年前

    首先,假设您有一个节点。您希望获得到根目录的路径。

    让我们将根路径表示为节点序列。

    我们可以构建这样一个方法:编写一个方法,该方法接受一个项和一个函数,该函数标识下一个项,并返回序列。

    public static IEnumerable<T> Path(T item, Func<T, T> nextItem) where T : class
    {
        T current = item;
        while(current != null)
        {
            yield return current;
            current = nextItem(current);
        }
    }
    

    现在,您可以在对象列表上编写查询。

    List<Node> nodes = whatever;
    var paths = from node in nodes 
                select Path(node, n=>GetNodeById(n.Parent));
    

    现在你有了一系列的节点。

    假设您需要一系列字符串:

    var namePaths = 
        from node in nodes 
        select (from pathElement in Path(node, n=>GetNodeById(n.Parent)) 
                select pathElement.Name);
    

    等等。

    有道理?