使用数组映射和 if 条件来过滤结果

99

我正在尝试使用数组映射来进一步过滤对象,以便准备将其发送到服务器进行保存。我可以过滤出一个键值,这很好,但我想再进一步检查它们是否与内部布尔值匹配。

目前,我有以下代码 -

$scope.appIds = $scope.applicationsHere.map( function(obj){
        if(obj.selected == true){
            return obj.id;
        }
    });

这个方法很好地提取了id,但是如果它们的选定值为false,我不想将它们推入这个新数组中,因此我加了一个条件进一步筛选。这在某种程度上有效,我得到了一个id的数组,但是那些.selected == false的id仍然在数组中,只是值为null。因此,如果对象中有4个项目且其中2个为false,则看起来像这样 -

 appIds = {id1, id2, null, null};

我的问题是,有没有一种方法可以在不放置空值的情况下完成这个操作。谢谢阅读!


2
在JS中,有一个名为Array.prototype.filter的方法可以实现这个目的。 - pawel
5个回答

160

你正在寻找.filter()函数:

  $scope.appIds = $scope.applicationsHere.filter(function(obj) {
    return obj.selected;
  });

这将生成一个仅包含“selected”属性为true(或真值)的对象的数组。

编辑 抱歉我去冲咖啡了,错过了评论 - 是的,正如jAndy在评论中指出的那样,要筛选然后只挑选出“id”值,应该是:

  $scope.appIds = $scope.applicationsHere.filter(function(obj) {
    return obj.selected;
  }).map(function(obj) { return obj.id; });

一些功能库(比如我认为没有得到足够关注的 Functional)有一个 .pluck() 函数,可以从对象列表中提取属性值,但是原生JavaScript缺乏这样的工具。


1
我能把appids转换成只有id而不是完整对象的数组吗?感谢您的帮助! - ajmajmajma
5
您需要在筛选后链接 .map(),这是完全可能的。 - jAndy
2
@user3201696,它看起来非常像.filter(function(obj) { return obj.selected; }).map(function(obj) { return obj.id1 });。这将创建一个新数组,其中仅包含id1属性的值。 - jAndy
1
@jAndy 噢,这正是我正在寻找的,非常感谢你!! - ajmajmajma
非常好的答案,感谢你们的帮助。快速跟进问题,您能否使用这些方法来执行相反的操作?因此,当我收到数组时,这些方法是否可以用于将id数组与对象进行比较,以将.selected设置为true(着陆时它们都是false)?我的初始想法是双重循环并将ids与对象中的ids进行比较,但我认为这不是最好的方法。再次感谢! - ajmajmajma
显示剩余3条评论

34
你应该使用 Array.prototype.reduce 来完成这个任务。 我做了一个 JS 性能测试,证明这比使用 .filter + .map 更快。
$scope.appIds = $scope.applicationsHere.reduce(function(ids, obj){
    if(obj.selected === true){
        ids.push(obj.id);
    }
    return ids;
}, []);

为了更加清晰,这是我在JSPerf测试中使用的.reduce示例:

  var things = [
    {id: 1, selected: true},
    {id: 2, selected: true},
    {id: 3, selected: true},
    {id: 4, selected: true},
    {id: 5, selected: false},
    {id: 6, selected: true},
    {id: 7, selected: false},
    {id: 8, selected: true},
    {id: 9, selected: false},
    {id: 10, selected: true},
  ];
  
   
var ids = things.reduce((ids, thing) => {
  if (thing.selected) {
    ids.push(thing.id);
  }
  return ids;
}, []);

console.log(ids)


编辑1

注意,截至2018年2月,在Chrome和Edge浏览器中,“Reduce + Push”是最快的,但在Firefox浏览器中比“Filter + Map”慢。



@DanMandel 是否有可能在第一次推送元素时停止推送?我的意思是,控制台上只应该打印一个值..? - Sivaprakash D
1
@SivaprakashD 你的意思是要找到第一个满足条件的实例吗?你可以使用 var id = things.find(thing => thing.selected),它会在找到第一个实例后停止搜索。https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find。或者使用类似的 .some 方法来获取一个 true/false 值,表示“这个对象是否存在于这个数组中”。 - Justin L.
@JustinL。感谢您的回复,我从另一个Stack Overflow问题中解决了这个问题... - Sivaprakash D
2
@DanMandel - 过滤器+映射怎么可能是O(n^2)?我认为它应该是O(2n),基本上就是O(n)。 - Vatsal
2
@DanMandel 的筛选器 + 映射肯定是 O(n) 而不是 n²。 - callback
在使用reduce函数时,几乎总是更安全的做法是提供initialValue参数。 - Rahul.S

22

您可以使用flatMap。它可以在一个函数中进行过滤和映射。

$scope.appIds = $scope.applicationsHere.flatMap(obj => obj.selected ? obj.id : [])

7

简单解决方案

SomeArrayValues.filter(x=> x.id !== idNameDetailsColorDto.id).map(ids => (ids.id))

4

以下是2019年的相关信息:

我认为,reduce和map+filter可能会有一些依赖于需要循环的内容。不确定,但reduce似乎要慢一些。

有一件事是肯定的-如果你想要性能提升,编写代码的方式非常重要!

这是一个JS性能测试,它展示了完全打出代码与检查“falsey”值(例如if(string){...} )或返回布尔值所期望的“falsey”值时的巨大改进。

希望这对某人有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接