【问题标题】:Moving specific elements after others but not to top or bottom在其他元素之后移动特定元素,但不移动到顶部或底部
【发布时间】:2017-02-28 11:40:32
【问题描述】:

假设我有一个数组

{ "w", "w", "z", "a", "c", "r", "f", "d", "e", "c", "g", "f", "m", "z" }

我有一条规则,所有的“c”都应该移到“f”之前。 在遵守规则的同时尽可能保持与原始顺序最接近的目标。 一个真实的例子是一个应用程序,它知道某个插件“A”应该高于加载列表中的某个插件“B”。

所以预期的结果是:

将 c 移到第一个 f 之前:

{ "w", "w", "z", "a", "c", "r", "c"<<, "f", "d", "e", "g", "f", "m", "z" }

在最后一个 c 之后移动 f:

{ "w", "w", "z", "a", "c", "r", "d", "e", "c", >>"f", "g", "f", "m", "z" }

是否可以使用 LINQ 或任何准备用于此目的的东西?

我尝试了OrderBy(x =&gt; x == "c" ? 0 : x == "f" ? 1 : (int?)null),但它会将元素移动到末尾。我只想对特定元素进行排序,但尽可能保持它们相对于其他元素的位置。

更新

该解决方案应该适用于任何输入:

    public static char GetLetter(Random rand)
    {
        string chars = "abcdefghijklmnopqrstuvwxyz";
        int num = rand.Next(0, chars.Length - 1);
        return chars[num];
    }

    public static IEnumerable<string> Sort(IEnumerable<string> enumerable, string first, string second)
    {
        ???

    }

    static void Main(string[] args)
    {
        Random rand = new Random();
        string[] input = Enumerable.Range(1, 10).Select(x => GetLetter(rand) + "").ToArray();
        var result = Sort(Sort(input, "c", "f"), "m", "e");
    }

支持多个元素(“m”、“c”、“f”)和多个规则是可取的,如果它不会使事情变得太复杂的话。

【问题讨论】:

  • 是否可以使用List&lt;string&gt; 代替?这允许您在指定的索引处插入/删除,这使得这更容易。它可以用数组完成,但比单行 LINQ 更难。
  • 这是发生在内存中还是 LINQ 被转换成 SQL?
  • @RobLang 在内存中
  • 您是否受限于使用阵列使用的相同内存块,或者您是否能够执行复制到新阵列的方法? (取决于你的实物大小)
  • @RobLang 没有具体的性能限制,复制就可以了

标签: c# arrays linq sorting


【解决方案1】:

这个方法做你想做的事:

public static IEnumerable<string> Sort(this IEnumerable<string> enumerable, string first, string second)
{
    return enumerable.TakeWhile(s => s != second)
        .Concat(enumerable.SkipWhile(s => s != second).OrderBy(a => a != first));
}

这个想法是首先获取列表的一部分,直到第一个f(在这种情况下)。然后将剩余部分附加到它上面,首先按c 字符排序。

一个缺点是列表被枚举了两次(部分)。如果这对性能至关重要,您应该选择只枚举一次的替代方法。

【讨论】:

    【解决方案2】:

    这是一个解决方案:

    string[] ch = {"a", "c", "f", "d", "e", "c", "g", "f", "m"};
    var res = ch.Select((item, index) => new { item, index })
                .OrderBy(x => x.item == "c" ? 0 : Convert.ToInt32(x.index))
                .Select(c => c.item).ToArray();
    

    输出:

    【讨论】:

    • 您的解决方案适合此特定输入,但您如何指定“f”?如果之后我想对“c”和“g”进行排序怎么办?你在这里只对“c”进行排序。
    • @Vlad 它根据你的规则工作:我有一个规则,所有的“c”都应该在“f”之前移动。所以它不关心@ 987654324@ 或 g 或 ..... 它首先对 c 字符进行排序,并始终产生您在问题中显示的第一个预期结果,无论 fg 或....
    • 你坚持特定的输入。规则是所有“c”都应该移到“f”之前,我需要能够将它应用于任何元素数组,而不仅仅是这个“a”,“c”, “f”、“d”、“e”、“c”、“g”、“f”、“m”.
    • @Vlad 你能显示另一个输入以便我们检查吗?
    • @Vlad 如果{ "a", "c", "c", "f", "f" , "d", "e", "g", "m" } 是所需的结果,您也可以像这样更改OrderByOrderBy(x =&gt; x.item == "c" ? 0 : x.item == "f" ? 1 : Convert.ToInt32(x.index)) 通过使用这种方式,您还可以检查f
    【解决方案3】:

    如果 In 正确理解了您的问题,我认为您需要有一个数组规则,您将在其中定义规则,例如应该先出现哪个字符。考虑到你的前辈,我在这里添加了一个测试。您可以修改规则并使用它。

        [Test]
        public void CustomOrderByTests()
        {
            char[] ch = { 'a', 'c', 'f', 'd', 'e', 'c', 'g', 'f', 'm' };
            char[] rules = { 'a', 'f', 'c' };
    
            var result = rules.Intersect(ch).Union(ch);
    
            char[] expected = { 'a', 'f', 'c', 'd', 'e', 'g', 'm' };
            CollectionAssert.AreEqual(result, expected);
        }
    

    如果你想在使用Array.Sort()重载之前在同一位置重复元素,它将缩短结果而不删除重复项

    Array.Sort(rules, ch);
    

    现在您的 ch 数组将具有排序数组。

    【讨论】:

    • 1.输出包含另一个不期望的元素计数。 2. 这只是将它们全部移动到序列的开头。但是一开始可能还有其他元素应该保留哪些位置。如果我有 'x', 'r', 'z', 'a', 'c', 'f', 'd', 'e', 'c', 'g', 'f' , 'm'?我编辑了问题。
    • 1.什么元素计数? 2. 如果您有另一个元素,请将其添加到规则数组中的正确位置。
    • 有 9 个元素,你的输出是 7 个元素。
    • 那些是重复的元素。在预期数组中,我们知道该重复数组的正确位置。你可以处理那个位置,不是吗?
    • 很抱歉让您感到困惑,请参阅更新问题中的示例,现在它们涵盖了所有情况。
    猜你喜欢
    • 1970-01-01
    • 2021-04-03
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-01
    相关资源
    最近更新 更多