使用功能方法从一系列数组中提取数据

时间:2017-09-01 02:52:08

标签: javascript arrays filter reduce

我需要对我收到的数据进行一些额外的过滤,这样我才能获得符合特定条件的数据。我试图找到一个运算符组合,我可以通过函数式编程方法来实现这一点。我想也许filterreduce的组合可能有效吗?我可以简单地将它们的组合链接在一起以获得我正在寻找的结果吗?

我的数据结构如下:

"data": [
    {
        "_id": "53545",
        "services": [
            {
                "service": "service1",
                "history": [
                    {
                        "_id": "6546",
                        "status": "stage1",
                        "completed": true
                    },
                    {
                        **"_id": "6547",
                        "status": "stage2",
                        "completed": false**
                    }
                  ],
            {
                "service": "service2",
                "history": [
                    {
                        "_id": "6743",
                        "status": "stage3",
                        "completed": false
                    },
                    {
                        "_id": "3742",
                        "status": "stage2",
                        "completed": true
                    }
                   ]
                },
                {
                "service": "service3",
                "history": [
                    {
                        "_id": "6448",
                        "status": "stage4",
                        "completed": false
                    },
                    {
                        "_id": "4443",
                        "status": "stage1",
                        "completed": true
                    }
              ]
        }
     ]

要明确,此处dataserviceshistory是所有数组。在上述数据的情况下,只有一个对象在"历史"应该通过测试,因为只有一个对象,其中"状态:stage2"并且"完成:false"。但这意味着,在"数据"数组,这个特殊的对象,一个带有" _id":" 53545"应该通过,因为测试返回true。最终,我想要从"数据"中的对象内返回一个数组。数组,像这样的匹配。

非常具体地说,就我而言,总有3项服务。在这3个中的每一个中,可能存在1个或多达30个或更多个历史对象。我需要浏览所有3"服务",并找到状态为" stage2"的历史对象。并且"完成:false"。因此,如果三个服务中的任何一个具有" status:stage2"的历史对象,则应返回true。和"完成:false"。

我绊倒了迭代数组中的数组。

我可以这样直接得到它:

let stage2Data = data.filter(data => data.services[0].history[1].status === 'stage2' && data.services[0].history[1].completed === false);

但是,我显然不知道这些不同数组中的项目编号将包含我正在寻找的值。那么解决这个问题的最佳方法是什么?我想一系列for-of循环也可以起作用,但是我想使用一种更实用的方法,并且正在考虑将" filter"和"减少",或者" map"可以在这里工作。我怎样才能返回"数据中的对象"数组其中" "status": "stage2""completed": false对于"历史"中的至少一个对象为真。阵列

3 个答案:

答案 0 :(得分:2)

根据您希望的结果数据,这里有一个开始。您可以使用几个嵌套过滤器来强制执行此操作:

result = data.filter((datum) => {
  return datum.services.filter((service) => {
    return service.history.filter((h) => {
      return h.completed && h.status === "stage2"
    }).length > 0;
  }).length > 0;
});

tl; dr - 按照任何具有至少一个在第2阶段完成的历史记录的服务的数据过滤主列表。

工作小提琴:http://jsbin.com/jozujezidu/edit?js,console,output

如果希望结果数组只包含通过过滤器的历史记录,则必须修改代码。另外,这是一个On ^ 3,所以如果这个数据可能很大,你应该修改这个方法。但这应该让你前进。

不要担心激进的功能。如果你能找到一个人们可以理解和维护的工作解决方案,那么这是一个良好的开端。

答案 1 :(得分:2)

我们一直在评论中来回试图在您的问题中获得有效的数据结构。让我举一个简单的例子,告诉你如何让每个人都更轻松。

首先,使用编辑框顶部的代码段按钮创建代码段。这个图标看起来像是一个典型的“折角”文档图标,里面有<>

这将打开一个包含HTML / CSS / JavaScript代码的弹出框。忽略HTML和CSS框并将数据对象粘贴到JavaScript框中。

因为它不是“原样”的JavaScript,所以将它包装在赋值语句中以获取可以使用的JavaScript变量。

然后在末尾添加console.log语句以显示数据。使用运行按钮确保代码实际解析并运行并正确显示数据。一旦你这样做,人们就会更容易 来帮助你。

这是一个简化的例子:

const input = {
    "data": [
        {
            "one": "two"
        },
        {
            "three": "four"
        }
    ]
};

console.log( JSON.stringify( input, null, 4 ) );

点击上方的运行代码段按钮,查看此示例运行。请注意它是如何打印出我上面使用的相同数据结构。

如果您在问题中执行相同操作,则会强制您清理数据以使其实际有效。然后你给人们一些他们可以使用的东西。

另一个注意事项:不要试图以“功能”的方式做这件事。通过使用let output = [];创建一个空数组,然后在每个嵌套数组(或.forEach()循环上使用for...of,如果您定位到最新的浏览器,您很可能会获得更易理解的代码),并在每个匹配元素上使用output.push()

为了说明,假设我正确理解了您的数据结构(假设您将其包含在input变量中,如我的简化示例中所示),它可能看起来像这样:

let output = [];
input.data.forEach( function( dataItem ) {
    dataItem.services.forEach( function( service ) {
        service.history.forEach( function( historyItem ) {
            if( ! historyItem.completed  && historyItem.status == "stage2" ) {
                output.push( historyItem );
            }
        });
    });
});

或使用for...of循环:

let output = [];
for( let dataItem of input.data ) {
    for( let service of dataItem.services ) {
        for( let historyItem of service.history ) {
            if( ! historyItem.completed  && historyItem.status == "stage2" ) {
                output.push( historyItem );
            }
        }
    }
}

当然,这些都是未经测试的。无论哪种方式,它都不是很花哨,但似乎很容易理解。

答案 2 :(得分:0)

过滤操作将基于与您的值匹配的谓词。

如果您想要服务对象列表,请尝试以下方法:

let result = data[0].services.filter((item) => item.history.some((h) => h.status=='stage2' && h.completed==false));