使用功能性方法从数组中抽出数据

问题描述:

我需要对获取的数据进行一些额外的过滤,以便我只获取符合特定条件的数据。我试图找到一个我可以用来通过函数式编程方法完成这个操作的操作符组合。我想也许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是所有阵列。在上述数据的情况下,“历史”中只有一个对象应该通过测试,因为只有一个对象,其中“status:stage2”和“completed:false”。但这意味着在“data”数组中,这个特定的对象,带有“_id”:“53545”的那个对象应该通过,因为测试返回true。最终,我想从“数据”数组中的对象中返回一个数组,它们就像这样匹配。

非常特别的是,在我的情况下,总是有3个服务。在这3个中,每个可能有1个或多达30个或更多的历史对象。我需要查看所有3个“服务”,并查找状态为“stage2”和“completed:false”的历史记录对象。因此,如果三个服务中的任何一个具有“status:stage2”和“completed:false”的历史记录对象,则这应该返回true。

我越来越迂回迭代数组内的数组。

我能以这种方式直接获得它:

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

不过,我显然不知道什么项目数在这些不同的阵列将包含我正在寻找的价值。那么解决这个问题的最好方法是什么?我想一系列for-of循环也可以工作,但我想使用更多功能的方法,并且正在考虑“过滤器”和“减少”的组合,或者“地图”可以在这里工作。我怎么会只有“数据”数组,其中“"status": "stage2""completed": false是真正的内至少对象的一个​​‘历史’阵内返回的对象?

+1

将数据的_complete_示例添加到问题中,同时显示另一个对象,显示要从中生成的输出。事实上,没有足够的输入数据能够真正理解其格式,并且很难准确猜测你想要输出的内容。 –

+0

为了澄清,当我说“完整”时,我并不是说如果数组中有一百个项目,则需要包含所有数百项。但是,为每个数组至少包含两个或三个元素,并使其成为一个完整的数据对象,以便某人可以无错地加载到JavaScript中。目前在问题中的对象在中途被切断。 –

+0

为了增加一些清晰度,“历史”数组中只有一个项目将被设置为“completed:false” - 因为一次只有一个活动阶段。因此,即使历史数组中有100个对象,也只会有一个“完成:假”的对象。 – Ademo

我们一直在回顾评论,试图在您的问题中获得有效的数据结构。让我给你一个简单的例子,说明如何让每个人都更容易。

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

这将打开一个弹出框,其中包含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); 
      } 
     } 
    } 
} 

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

+0

谢谢。使用您描述的方法(使用forEach())来简单实现嵌套数组检查会是什么样子? – Ademo

+1

我添加了几个_untested_例子,一个使用'.forEach()',另一个使用'for ... of'循环。希望这有帮助! –

+0

非常感谢,迈克尔。这种做法看起来很有希 – Ademo

过滤操作将基于谓词匹配你的价值观。

如果你是想服务对象的列表试试这个方法:

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

+0

但是,这只会得到数据数组中的第一个对象,对吧?而我需要迭代该数组中的所有项目。 – Ademo

+0

没有办法从问题中知道顶层数组中有多少个对象,只需要在data [] array –

+0

中存在多个对象的情况下添加另一个过滤器。我想我的主要问题是如何通过多个嵌套级别的数组进行筛选 - 并且隐式地意味着不必知道我需要针对哪个数组中的哪个元素 - 针对这些数组中的任何元素 - 提前。 – Ademo

根据您想要的结果数据,这里是你开始你可以蛮力这跟。几个嵌套过滤器:

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;博士 - 由有有在阶段完成2

工作小提琴至少一条历史记录服务的任何数据过滤器的主列表:http://jsbin.com/jozujezidu/edit?js,console,output

如果你想你生成的数组只有通过过滤器的历史记录,您必须修改代码。另外,这是一个On^3,所以如果这个数据可能很大,你应该修改这个方法。但这应该让你走。

不要担心武装功能。如果你能找到一个人们可以理解和维护的解决方案,那么这是一个好的开始。

+0

谢谢,这是正确的方向。但是,是的,我只需要提取通过测试的记录。现在我只是回来我的原始列表。所以我正在查看这个,看看我需要做些什么才能得到传递给我的结果。 – Ademo

+0

所以,我很清楚,在上面的例子中返回的代码到底是什么?另外,在你的小提琴示例中,你创建的两个对象都已经“完成”设置为“true”,而其中一个应该是“false”。而当我改变一个“完成:假”我得到一个空数组返回。 – Ademo