如何仅对数组中特定元素排序且保留非排序元素原位置?
混合类型数组指定元素原位排序实现
需求说明
现有包含多类型元素的数组示例:
ary = [3, "foo", 2, 5, "bar", 1, "baz", 4]
需要仅对数组内符合条件的元素(本例为整数,可扩展为任意类型/自定义筛选规则)排序,非目标元素必须完全保留在原有索引位置,排序后的目标元素按顺序填充所有原本属于目标类型的位置。
预期排序结果:
[3, "foo", 2, 5, "bar", 1, "baz", 4] # 原数组 [1, "foo", 2, 3, "bar", 4, "baz", 5] # 排序后
排序后"foo"仍在索引1、"bar"仍在索引4、"baz"仍在索引6,位置不发生任何变化。
原实现通过拆分带索引的目标/非目标元素集合、排序后逐次插入非目标元素的方式实现,逻辑繁琐且多次插入性能较差,以下是更简洁优雅的实现方案。
推荐实现:枚举器填充法
仅需2行核心代码即可完成需求,不需要手动处理索引插入:
# 提取所有目标元素排序后转为枚举器 sorted_target = ary.grep(Integer).sort.each # 遍历原数组,目标元素取排序后的值替换,非目标元素直接保留 result = ary.map { |item| item.is_a?(Integer) ? sorted_target.next : item }
运行结果和预期完全一致:
# => [1, "foo", 2, 3, "bar", 4, "baz", 5]
这个方案的优势:
- 逻辑直观,没有拆分集合、逐次插入的冗余操作
- 性能优异,时间复杂度由排序步骤主导为O(nlogn),避免了多次
insert带来的O(n²)开销 - 扩展性强,只需要修改筛选条件即可适配任意排序规则,比如要排序所有长度大于2的字符串,只需要把
grep(Integer)和判断逻辑替换为对应的字符串规则即可。
可选实现:索引映射法
如果不习惯使用枚举器,也可以先提取所有目标元素的索引,再做映射填充:
# 找到所有目标元素的索引位置 target_indexes = ary.each_index.filter { |i| ary[i].is_a?(Integer) } # 提取对应位置的元素排序 sorted_values = ary.values_at(*target_indexes).sort # 按索引填充,目标位置取排序后的值,非目标位置保留原值 result = ary.map.with_index { |item, idx| target_indexes.include?(idx) ? sorted_values.shift : item }
这个方案的逻辑和枚举器法一致,只是通过索引匹配的方式完成填充,运行结果完全相同。
内容的提问来源于stack exchange,提问作者Stefan
相关产品推荐
相关产品推荐

