如何使用 LINQ 创建函数来获取指定节点?

发布于 2024-11-30 02:31:29 字数 4535 浏览 0 评论 0 原文

我正在使用来自另一个网站的代码:

如何在数据库中对此类进行建模?

我在每个目标记录中都有一个名为“Rank”的字段。它告诉我什么是职位。例如:

Objective "Geometry": Rank1
|_Objective "Squares": Rank1
|_Objective "Circles": Rank2
|_Objective "Triangle": Rank3
  |_Objective "Types": Rank1
Objective "Algebra": Rank2
Objective "Trigonometry": Rank3

该排名告诉我节点的顺序。但我想获得所有排名:

Objective "Geometry": Rank1
|_Objective "Squares": Rank1   -> 1.1
|_Objective "Circles": Rank2
|_Objective "Triangle": Rank3
  |_Objective "Types": Rank1   -> 1.3.1
Objective "Algebra": Rank2
Objective "Trigonometry": Rank3    -> 3

我正在使用 LINQ to SQL。

<TreeView Name="treeView1">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate DataType="{x:Type data:Objective}" ItemsSource="{Binding Path=Objectives}" >
            <TextBlock Text="{Binding Name}" />
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

我需要一个 linq 函数来获取指定的节点。我的意思是,一个通过级别 (1.2)、(1.3.1) 获取节点的函数,

如果存在,则返回节点,如果不为空。

更新1:

这并不是一个真正的函数,但我意识到最好创建一个 getNode 函数。

    private void AddButton_Click(object sender, RoutedEventArgs e)
    {
        NorthwindDataContext cd = new NorthwindDataContext();

        int[] levels = LevelTextBox.Text.ToIntArray('.');
        string newGroupName = NameTextBox.Text;

        Objective currentObjective = null;
        int? identity = null;

        for (int i = 0; i < levels.Length - 1; i++ )
        {
            int currentRank = levels[i];

            if (identity == null)
            {
                currentObjective = (from p in cd.Objective
                                    where p.Level == currentRank && p.Parent_ObjectiveID == null
                                    select p).SingleOrDefault();
            }
            else
            {
                currentObjective = (from p in cd.Objective
                                    where p.Level == currentRank && p.Parent_ObjectiveID == identity
                                    select p).SingleOrDefault();
            }

            if (currentObjective == null)
            {
                MessageBox.Show("Levels don't exist");
                return;
            }
            else
            {
                identity = currentObjective.ObjectiveID;
            }
        }

        if (currentObjective != null)
        {
            if (levels.Last() == currentObjective.Level)
            {
                MessageBox.Show("Level already exists");
                return;
            }
        }
        else
        {
            var aux = (from p in cd.Objective
                       where p.Parent_ObjectiveID == null && p.Level == levels.Last()
                       select p).SingleOrDefault();

            if (aux != null)
            {
                MessageBox.Show("Level already exists");
                return;
            }
        }

        var newObjective = new Objective();
        newObjective.Name = NameTextBox.Text;
        newObjective.Level = levels.Last();
        newObjective.Parent_ObjectiveID = currentObjective == null ? null : (int?)currentObjective.ObjectiveID ;

        cd.Objective.InsertOnSubmit(newObjective);
        cd.SubmitChanges();
   }

更新2:

    public Objective GetNode(params int[] indexes)
    {
        return GetNode(null, 0, indexes);
    }

    public Objective GetNode(int? parentid, int level, params int[] indexes)
    {
        NorthwindDataContext cd = new NorthwindDataContext();
        Objective item = null;

        if (indexes.Length == 0)
            return null;

        if (parentid == null)
        {
            item = (from p in cd.Objective
                    where p.Level == indexes[level] && p.Parent_ObjectiveID == null
                    select p).SingleOrDefault();

        }
        else
        {
            item = (from p in cd.Objective
                    where p.Level == indexes[level] && p.Parent_ObjectiveID == parentid
                    select p).SingleOrDefault();
        }

        if (item == null)
            return null;

        if (++level < indexes.Length)
            item = GetNode(item.ObjectiveID, level, indexes);

        return item;
    }

I'm using this code from the another web:

How can I model this class in a database?

I have in each objective record a field named "Rank". It tells me what position is. For instance:

Objective "Geometry": Rank1
|_Objective "Squares": Rank1
|_Objective "Circles": Rank2
|_Objective "Triangle": Rank3
  |_Objective "Types": Rank1
Objective "Algebra": Rank2
Objective "Trigonometry": Rank3

That rank tells me the order of the nodes. But I want to get all the rank:

Objective "Geometry": Rank1
|_Objective "Squares": Rank1   -> 1.1
|_Objective "Circles": Rank2
|_Objective "Triangle": Rank3
  |_Objective "Types": Rank1   -> 1.3.1
Objective "Algebra": Rank2
Objective "Trigonometry": Rank3    -> 3

I'm using LINQ to SQL.

<TreeView Name="treeView1">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate DataType="{x:Type data:Objective}" ItemsSource="{Binding Path=Objectives}" >
            <TextBlock Text="{Binding Name}" />
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

I need a linq function where I can get a specified node. I mean, a function which gets the node through the level (1.2), (1.3.1)

If exists, return the node, if not null.

UPDATE 1:

This is not really a function, but I realized it's better to create a getNode function.

    private void AddButton_Click(object sender, RoutedEventArgs e)
    {
        NorthwindDataContext cd = new NorthwindDataContext();

        int[] levels = LevelTextBox.Text.ToIntArray('.');
        string newGroupName = NameTextBox.Text;

        Objective currentObjective = null;
        int? identity = null;

        for (int i = 0; i < levels.Length - 1; i++ )
        {
            int currentRank = levels[i];

            if (identity == null)
            {
                currentObjective = (from p in cd.Objective
                                    where p.Level == currentRank && p.Parent_ObjectiveID == null
                                    select p).SingleOrDefault();
            }
            else
            {
                currentObjective = (from p in cd.Objective
                                    where p.Level == currentRank && p.Parent_ObjectiveID == identity
                                    select p).SingleOrDefault();
            }

            if (currentObjective == null)
            {
                MessageBox.Show("Levels don't exist");
                return;
            }
            else
            {
                identity = currentObjective.ObjectiveID;
            }
        }

        if (currentObjective != null)
        {
            if (levels.Last() == currentObjective.Level)
            {
                MessageBox.Show("Level already exists");
                return;
            }
        }
        else
        {
            var aux = (from p in cd.Objective
                       where p.Parent_ObjectiveID == null && p.Level == levels.Last()
                       select p).SingleOrDefault();

            if (aux != null)
            {
                MessageBox.Show("Level already exists");
                return;
            }
        }

        var newObjective = new Objective();
        newObjective.Name = NameTextBox.Text;
        newObjective.Level = levels.Last();
        newObjective.Parent_ObjectiveID = currentObjective == null ? null : (int?)currentObjective.ObjectiveID ;

        cd.Objective.InsertOnSubmit(newObjective);
        cd.SubmitChanges();
   }

UPDATE 2:

    public Objective GetNode(params int[] indexes)
    {
        return GetNode(null, 0, indexes);
    }

    public Objective GetNode(int? parentid, int level, params int[] indexes)
    {
        NorthwindDataContext cd = new NorthwindDataContext();
        Objective item = null;

        if (indexes.Length == 0)
            return null;

        if (parentid == null)
        {
            item = (from p in cd.Objective
                    where p.Level == indexes[level] && p.Parent_ObjectiveID == null
                    select p).SingleOrDefault();

        }
        else
        {
            item = (from p in cd.Objective
                    where p.Level == indexes[level] && p.Parent_ObjectiveID == parentid
                    select p).SingleOrDefault();
        }

        if (item == null)
            return null;

        if (++level < indexes.Length)
            item = GetNode(item.ObjectiveID, level, indexes);

        return item;
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

[旋木] 2024-12-07 02:31:29

编辑:

您可能最好传入 NorthwindDataContext 实例,而不是每次传递都创建一个新实例。

您可以通过创建如下方法来做到这一点,该方法已经过重构,因此不需要递归,这应该对可读性有所帮助。

    public Objective GetNode(IEnumerable<Objective> collection, params int[] indices)
    {
        Objective current = null;

        for (int t = 0; t < indices.Length; t++)
        {
            Objective item = collection.SingleOrDefault(x => x.Parent == current && x.Rank == indices[t] - 1);

            if (item == null)
                return null;
        }

        return current;
    }

调用方式如下: GetNode(cd.Objective, LevelTextBox.Text.ToIntArray());


原始:
您可以使用类似的方法,它只是一个简单的扩展方法:

    public static TreeViewItem Get(this TreeView tree, params int[] indexes)
    {
        if (tree == null)
            return null;

        if (indexes == null || indexes.Length == 0)
            return null;

        TreeViewItem i = tree.Items[indexes[0] - 1] as TreeViewItem;

        for (int index = 1; index < indexes.Length; index++)
        {
            i = i.Items.Count >= indexes[index] - 1 ? i.Items[indexes[index] - 1] as TreeViewItem : null;

            if (i == null)
                return null;
        }

        return i;
    }

并且将由 treeView1.Get(1,3,1); 使用,或者在您编辑的情况下,treeView1.Get(1,3,1); 使用。 Get(LevelTextBox.Text.Split('.').Select(x => int.Parse(x)).ToArray()); 但是,这有错误处理对于无效输入。

如果您不能确定所有项目都是 TreeViewItem 对象,您可以将 tree.Items[...] 替换为 tree.ItemContainerGenerator.ContainerFromIndex(...)< /code> (与 i.Items 相同)

但是,这些更改将要求 TreeView 已完全呈现。

Edit:

You're probably best to pass in an instance of the NorthwindDataContext versus creating a new one with each pass.

You could do this by creating a method as below, which has been refactored so that it doesn't need to be recursive which should help a little in the readability department.

    public Objective GetNode(IEnumerable<Objective> collection, params int[] indices)
    {
        Objective current = null;

        for (int t = 0; t < indices.Length; t++)
        {
            Objective item = collection.SingleOrDefault(x => x.Parent == current && x.Rank == indices[t] - 1);

            if (item == null)
                return null;
        }

        return current;
    }

To be called like: GetNode(cd.Objective, LevelTextBox.Text.ToIntArray());


Original:
You could use something like this, it's just a simple Extension method:

    public static TreeViewItem Get(this TreeView tree, params int[] indexes)
    {
        if (tree == null)
            return null;

        if (indexes == null || indexes.Length == 0)
            return null;

        TreeViewItem i = tree.Items[indexes[0] - 1] as TreeViewItem;

        for (int index = 1; index < indexes.Length; index++)
        {
            i = i.Items.Count >= indexes[index] - 1 ? i.Items[indexes[index] - 1] as TreeViewItem : null;

            if (i == null)
                return null;
        }

        return i;
    }

And would be used by treeView1.Get(1,3,1); or in the case of your edit, treeView1.Get(LevelTextBox.Text.Split('.').Select(x => int.Parse(x)).ToArray()); however, this has zero error handling for invalid input.

If you can't be certain that all the items will be TreeViewItem objects, you can replace the tree.Items[...] with tree.ItemContainerGenerator.ContainerFromIndex(...) (and same with i.Items

These changes will require the TreeView to have been rendered fully, however.

撩动你心 2024-12-07 02:31:29

这是执行此操作的 LINQ 方法。

我假设了 Objective 的定义如下:

public class Objective
{
    public int ObjectiveId { get; set; }
    public int? Parent_ObjectiveId { get; set; }
    public string Name { get; set; }
    public int Rank { get; set; }
}

然后我创建了一个名为 LevelObjective 的支持类来捕获级别(即“1.3.1”),如下所示:

public class LevelObjective
{
    public Objective Objective { get; set; }
    public string Level { get; set; }
}

我已经开始定义了一系列目标:

var objectives = new []
{
    new Objective { ObjectiveId = 1, Parent_ObjectiveId = null, Name = "Geometry", Rank = 1, },
    new Objective { ObjectiveId = 2, Parent_ObjectiveId = 1, Name = "Squares", Rank = 1, },
    new Objective { ObjectiveId = 3, Parent_ObjectiveId = 1, Name = "Circles", Rank = 2, },
    new Objective { ObjectiveId = 4, Parent_ObjectiveId = 1, Name = "Triangle", Rank = 3, },
    new Objective { ObjectiveId = 5, Parent_ObjectiveId = 4, Name = "Types", Rank = 1, },
    new Objective { ObjectiveId = 6, Parent_ObjectiveId = null, Name = "Algebra", Rank = 2, },
    new Objective { ObjectiveId = 7, Parent_ObjectiveId = null, Name = "Trigonometry", Rank = 3, },
};

接下来,我创建了一个查找来从任何 id 中获取子项。

var lookup = objectives.ToLookup(x => x.Parent_ObjectiveId);

我使用此查找来创建一组顶级目标:

var roots = lookup[null]
    .Select(o => new LevelObjective()
    {
        Objective = o,
        Level = o.Rank.ToString(),
    });

然后定义了一个使层次结构扁平化的函数:

Func<
    IEnumerable<LevelObjective>,
    Func<LevelObjective, IEnumerable<LevelObjective>>,
    IEnumerable<LevelObjective>> flatten = null;

flatten = (rs, f) =>
    rs.Concat(
        from r in rs
        from c in flatten(f(r), f)
        select c);

我已经将其中一个定义为使用泛型的扩展方法,但我只是将其重构为 lambda 表达式使用了LevelObjective。

我现在定义了获取任何 LevelObjective 的子级所需的 Func>

Func<LevelObjective, IEnumerable<LevelObjective>> getChildren = lo =>
    from o in lookup[lo.Objective.ObjectiveId]
    select new LevelObjective()
    {
        Objective = o,
        Level = String.Format("{0}.{1}", lo.Level, o.Rank),
    };

然后,我可以根据原始的 Objective 对象集创建一个完整的 LevelObjective 对象列表。

var levelObjectives = flatten(roots, getChildren);

最后我可以把它变成一张从关卡到目标的地图。

var map = levelObjectives.ToLookup(x => x.Level, x => x.Objective);

现在,要找到任何目标,我只需调用此代码:

var objective = map["1.3.1"].FirstOrDefault();

现在我有一个函数,将为任何提供的级别键返回零个或多个目标。好处是,这只会对数据库执行一次查询,并且对 map 函数的调用会在 o(1) 时间内返回。

这对你有用吗?

Here's a LINQ way of doing this.

I've assumed a definition of Objective like so:

public class Objective
{
    public int ObjectiveId { get; set; }
    public int? Parent_ObjectiveId { get; set; }
    public string Name { get; set; }
    public int Rank { get; set; }
}

I've then created a support class called LevelObjective to capture the level (ie "1.3.1") like so:

public class LevelObjective
{
    public Objective Objective { get; set; }
    public string Level { get; set; }
}

And I've begun with a collection of objectives defined so:

var objectives = new []
{
    new Objective { ObjectiveId = 1, Parent_ObjectiveId = null, Name = "Geometry", Rank = 1, },
    new Objective { ObjectiveId = 2, Parent_ObjectiveId = 1, Name = "Squares", Rank = 1, },
    new Objective { ObjectiveId = 3, Parent_ObjectiveId = 1, Name = "Circles", Rank = 2, },
    new Objective { ObjectiveId = 4, Parent_ObjectiveId = 1, Name = "Triangle", Rank = 3, },
    new Objective { ObjectiveId = 5, Parent_ObjectiveId = 4, Name = "Types", Rank = 1, },
    new Objective { ObjectiveId = 6, Parent_ObjectiveId = null, Name = "Algebra", Rank = 2, },
    new Objective { ObjectiveId = 7, Parent_ObjectiveId = null, Name = "Trigonometry", Rank = 3, },
};

Next I created a look up to get the children from any id.

var lookup = objectives.ToLookup(x => x.Parent_ObjectiveId);

I used this look up to create a set of the top-level objectives:

var roots = lookup[null]
    .Select(o => new LevelObjective()
    {
        Objective = o,
        Level = o.Rank.ToString(),
    });

I then defined a function that flattens a hierarchy:

Func<
    IEnumerable<LevelObjective>,
    Func<LevelObjective, IEnumerable<LevelObjective>>,
    IEnumerable<LevelObjective>> flatten = null;

flatten = (rs, f) =>
    rs.Concat(
        from r in rs
        from c in flatten(f(r), f)
        select c);

I already had one of these defined as an extension method that used generics, but I just refactored into a lambda expression that used LevelObjective.

I now defined the Func<LevelObjective, IEnumerable<LevelObjective>> required to get the children of any LevelObjective.

Func<LevelObjective, IEnumerable<LevelObjective>> getChildren = lo =>
    from o in lookup[lo.Objective.ObjectiveId]
    select new LevelObjective()
    {
        Objective = o,
        Level = String.Format("{0}.{1}", lo.Level, o.Rank),
    };

I could then create a complete list of LevelObjective objects based on the original set of Objective objects.

var levelObjectives = flatten(roots, getChildren);

Finally I can turn this into a map from level to objectives.

var map = levelObjectives.ToLookup(x => x.Level, x => x.Objective);

Now, to find any objective I just have to call this code:

var objective = map["1.3.1"].FirstOrDefault();

So now I have a function that will return zero or more objectives for any supplied level key. The nice thing is that this will perform only one query on the database and calls to the map function are returned in o(1) time.

Does this work for you?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文