简体   繁体   English

如何根据深度嵌套在数组和对象的某种组合中的日期找到最新的对象?

[英]How do I find the latest object based on a date that is deeply nested in some combination of arrays and objects?

I have a particularly challenging data structure (that I don't control) where I need to find the latest object based on a combination of nested arrays and objects, while some of the properties along the way are optional.我有一个特别具有挑战性的数据结构(我无法控制),我需要根据嵌套数组和对象的组合找到最新的对象,而沿途的一些属性是可选的。 So far, I've managed to filter away where the optional properties definitely exist, but convincing javascript to pull the dates up so I can compare them to find the latest is proving challenging.到目前为止,我已经设法过滤掉了肯定存在可选属性的地方,但是说服 javascript 拉出日期以便我可以比较它们以找到最新版本证明是具有挑战性的。

Here's the data structure:这是数据结构:

const data = {
  "objects": [
    {
      "id": "id1",
      "code": "code1",
      "props": [
        {
          "foos": [
            {
              "itemId": "theItem",
              "fooDate": "2018-03-01T10:00:00.000Z"
            }
          ]
        }
      ]
    },
    {
      "id": "id2",
      "code": "code2",
      "props": [
        {
          "bar": {
            "itemId": "theItem",
            "barDate": "2018-03-06T22:00:00.000Z"
          }
        }
      ]
    },
    {
      "id": "id3",
      "code": "code3",
      "props": [
        {
          "foos": [
            {
              "itemId": "someOtherItem",
              "fooDate": "2018-01-23T06:00:00.000Z"
            },
            {
              "itemId": "theItem",
              "fooDate": "2018-03-04T18:00:00.000Z"
            }
          ]
        }
      ]
    }
  ]
}

const searchItem = "theItem";

const foosOnly = data.objects.filter(obj => obj.props.find(prop => prop.foos && prop.foos.find(foo => foo.itemId === searchItem)));

What I'm trying to find is the object with the most recent fooDate for "theItem" so that I can operate on some of the top-level properties of the object like id or code .我要查找的是具有最新fooDate的“theItem”对象,以便我可以对对象的某些顶级属性(如idcode In this example, I expect "id3" to be the most recent despite also having "someOtherItem" with an older fooDate .在这个例子中,我希望“id3”是最新的,尽管还有“someOtherItem”和较旧的fooDate

I've managed to remove objects that do not contain a foo with filter and find, but I struggle to act on fooDate in comparison between the foos so I can find the latest foo .我已经成功地删除不包含对象foo与过滤器,并找到,但我很难作用于fooDate在比较foos这样我就可以找到最新的foo

The format of fooDate means you can compare it lexicographically. fooDate的格式意味着您可以按字典顺序比较它。 Looking for fooDate just means doing a lot of guards and loops:寻找fooDate只是意味着做很多守卫和循环:

let latest = null;
let latestDate = null;
for (const item of data.objects) {
    if (Array.isArray(item.props)) {
        for (const prop of item.props) {
            if (prop.foo) {
                for (const {fooDate} of prop.foo) {
                    if (fooDate && (!latestDate || fooDate > latestDate)) {
                        latest = item;
                        latestDate = fooDate;
                    }
                }
            }
        }
    }
}

Live Example:现场示例:

 const data = { "objects": [ { "id": "id1", "code": "code1", "props": [ { "foo": [ { "itemId": "theItem", "fooDate": "2018-03-01T10:00:00.000Z" } ] } ] }, { "id": "id2", "code": "code2", "props": [ { "bar": [ { "itemId": "theItem", "barDate": "2018-03-06T22:00:00.000Z" } ] } ] }, { "id": "id3", "code": "code3", "props": [ { "foo": [ { "itemId": "someOtherItem", "fooDate": "2018-01-23T06:00:00.000Z" }, { "itemId": "theItem", "fooDate": "2018-03-04T18:00:00.000Z" } ] } ] } ] }; let latest = null; let latestDate = null; for (const item of data.objects) { if (Array.isArray(item.props)) { for (const prop of item.props) { if (prop.foo) { for (const {fooDate} of prop.foo) { if (fooDate && (!latestDate || fooDate > latestDate)) { latest = item; latestDate = fooDate; } } } } } } console.log(latest);

I started going down the route of navigating the object hierarchy, but there seemed like too many unknowns.我开始沿着导航对象层次结构的路线走下去,但似乎有太多的未知数。 So, if you're only curious about a property called fooDate , here's a solution that loops through the objects, stringify's each one, and uses regex to get the date:因此,如果您只对名为fooDate的属性感到好奇,那么这里有一个解决方案,它循环遍历对象,对每个对象进行字符串化,并使用正则表达式获取日期:

let latest = null;
data.objects.forEach(obj => {
    const json = JSON.stringify(obj);
    const regex = new RegExp(/^.+"fooDate":"(.+)".+$/);
    const match = json.match(regex);
    if (match && match.length > 1) {
        const [first, ...results] = match;
        results.forEach(result => {
            const timestamp = new Date(result).valueOf();
            if (!latest || latest.timestamp < timestamp) {
                latest = { obj, timestamp };
            }
        });
    }
});

Below code should help you achieve the desired object.下面的代码应该可以帮助您实现所需的对象。 If it works, I 'll add comments too.如果有效,我也会添加评论。

 const data={"objects":[{"id":"id1","code":"code1","props":[{"foos":[{"itemId":"theItem","fooDate":"2018-03-01T10:00:00.000Z"}]}]},{"id":"id2","code":"code2","props":[{"bar":{"itemId":"theItem","barDate":"2018-03-06T22:00:00.000Z"}}]},{"id":"id3","code":"code3","props":[{"foos":[{"itemId":"someOtherItem","fooDate":"2018-01-23T06:00:00.000Z"},{"itemId":"theItem","fooDate":"2018-03-04T18:00:00.000Z"}]}]}]}; const list = data.objects; const desiredObject = list.sort((itemA, itemB) => { let itemAFoo = itemA.props.reduce((list, prop) => {return list.concat(prop.foos || [])}, []).sort((a, b) => a.fooDate - b.fooDate).pop() || {fooDate: 0}; let itemBFoo = itemA.props.reduce((list, prop) => {return list.concat(prop.foos || [])}, []).sort((a, b) => a.fooDate - b.fooDate).pop() || {fooDate: 0}; return itemAFoo.fooDate - itemBFoo.fooDate; }).pop(); desiredItemId = desiredObject.props.reduce((list, prop) => {return list.concat(prop.foos || [])}, []).pop().itemId; console.log('desiredObject:', desiredObject); console.log('desiredItemId:', desiredItemId);

Here's some code for what I think would fix your issue.这是我认为可以解决您的问题的一些代码。 The objective of this method would be to create a new data structure of type此方法的目标是创建一个新的数据结构类型

{
  id: string;
  itemId: string;
  date: Date;
}

after which you can simply filter/sort and search.之后,您可以简单地过滤/排序和搜索。 So let's see how we can create above data.那么让我们看看如何创建上述数据。

Something along the lines of类似的东西

const intermediateData = [];

function findItemIdAndDateRecursively(id, object) {
  Object.keys(object).forEach((key) => {
    if(key === 'itemId') {
      let date;
      for(let i in object) {
        if (isUTCString(object[i])) {
          date = object[i];
        }
      }
      intermediateData.push({id: id, itemId: object.itemId, date: date})
    } else if (typeof object[key] === 'object' || typeof object[key] === 'array') {
      findItemIdAndDateRecursively(id, object[key]);
    }
  })
}

array.forEach(item => {
  findItemIdAndDateRecursively(item.id, item);
})

where isUTCString is supposed to find if a string is a date string.其中 isUTCString 应该查找字符串是否为日期字符串。 You might have to write that utility function depending on the type of data you get.您可能必须根据获得的数据类型编写该实用程序函数。

You could wirte a function that receives the object end returns you the fooDate each time.您可以编写一个接收对象 end 每次返回 fooDate 的函数。 This function could do something like breadth-first-search to find this key in the object.这个函数可以做一些类似广度优先搜索的事情来在对象中找到这个键。

When you have this function you can iterate over the array ob objects and save the latest one you found.当您拥有此函数时,您可以遍历数组 ob 对象并保存您找到的最新对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM