使用函数式方法从一系列数组中提取数据

Using a functional approach to pull data out from series of arrays

我需要对获得的数据进行一些额外的过滤,这样我才能获得符合特定条件的数据。我正在尝试找到可用于通过函数式编程方法实现此目的的运算符组合。我在想 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都是数组。并且在上述数据的情况下,"history"中只有一个对象应该通过测试,因为只有一个对象位于"status:stage2"和"completed:false"中。但这意味着,在 "data" 数组中,这个特定对象,带有“_id”:“53545”的对象应该通过,因为测试 return 为真。最终,我想要从 "data" 数组中的对象中 return 一个数组,像这个一样匹配。

非常具体,在我的例子中,总是有 3 个服务。在这 3 个中的每一个中,可能有 1 个或多达 30 个或更多历史对象。我需要查看所有 3 个 "services",并找到状态为 "stage2" 和 "completed:false" 的历史对象。因此,如果这三个服务中的任何一个具有 "status:stage2" 和 "completed:false".

的历史对象,这应该 return 为真

我在遍历数组中的数组时被绊倒了。

我可以通过这种方式直接获取:

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

但是,我显然不知道这些不同数组中的哪个项目编号将包含我要查找的值。那么解决这个问题的最佳方法是什么?我想一系列 for-of 循​​环也可以工作,但我想使用更实用的方法,并且正在考虑 "filter" 和 "reduce" 的组合,或者 "map" 可以在这里工作。我如何只 return "data" 数组中的对象,其中“"status": "stage2""completed": false 对于 "history" 数组中的至少一个对象是正确的?

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

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

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

根据您想要的结果数据,这里是您的开始。你可以用一些嵌套的过滤器来暴力破解它:

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 阶段完成的历史记录。

工作fiddle:http://jsbin.com/jozujezidu/edit?js,console,output

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

不用担心好战的功能。如果您能找到一个人们可以理解和维护的工作解决方案,那么这是一个好的开始。

我们一直在评论中反复讨论,以尝试在您的问题中获得有效的数据结构。让我举一个简单的例子来说明如何让每个人都更容易做到这一点。

首先,使用编辑框顶部的片段按钮创建片段。该图标看起来像典型的 "folded corner" 文档图标,里面有 <>

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

因为它不是 JavaScript "as is",所以将其包装在赋值语句中以获得可以使用的 JavaScript 变量。

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

这是一个简化的例子:

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

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

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

如果您在问题中也这样做,这将迫使您清理数据以使其真正有效。然后你给人们一些他们可以使用的东西。

另一条注意事项:不要挂断尝试以 "functional" 方式执行此操作。通过使用 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 );
            }
        }
    }
}

当然,这两个都未经测试。不管怎样,它并不花哨,但似乎很容易理解。