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

时间:2019-03-05 07:40:41

标签: javascript node.js

我有一个特别具有挑战性的数据结构(我不控制),在该结构中,我需要基于嵌套数组和对象的组合来查找最新的对象,而沿途的某些属性是可选的。到目前为止,我已经设法过滤掉了可选属性的确存在的位置,但是说服javascript将日期拉长,以便我可以比较它们以找到最新的属性,这是充满挑战的。

这是数据结构:

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)));

我要查找的是对象“ theItem”中最新的fooDate对象,以便可以对对象的某些顶级属性进行操作,例如id或{ {1}}。在此示例中,尽管“ id3”也具有较旧的code,但我希望“ id3”是最新的。

我已经设法通过过滤器和查找来删除不包含fooDate的对象,但是在foo之间进行比较时,我很难对fooDate进行操作,因此我可以找到最新的foos

5 个答案:

答案 0 :(得分:1)

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script> <div ng-app="surveyApp" ng-controller="surveyCtrl"> <div ng-repeat="questionSet in questionSet"> <div ng-if="question_index == $index"> <div ng-repeat="onePageQuestions in questionSet.onePageQuestions"> <div ng-repeat="question in onePageQuestions.question"> {{question.description}} <form action=""> <div ng-repeat="options in question.options"> <input ng-change="saveAnswers(question,options.answer)" type="radio" name="gender" ng-model="$parent.answer" ng-value="options.answer"> {{options.answer}} </div> </form> </div> </div> </div> </div> <hr> <button ng-click="next()">Next</button> <button ng-click="submitAnswers()"> Submit</button>的格式意味着您可以按字典顺序进行比较。寻找fooDate只是意味着要进行大量的保护和循环操作:

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;
                    }
                }
            }
        }
    }
}

答案 1 :(得分:1)

我开始沿着导航对象层次结构的路线前进,但是似乎有太多的未知数。因此,如果您只是对名为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 };
            }
        });
    }
});

答案 2 :(得分:0)

下面的代码应该可以帮助您实现所需的目标。如果可行,我也会添加评论。

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);

答案 3 :(得分:0)

以下是一些我认为可以解决您问题的代码。该方法的目的是创建一个新的类型的数据结构

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

之后,您可以简单地进行过滤/排序和搜索。因此,让我们看看如何创建以上数据。

类似的东西

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);
})

其中isUTCString应该用来查找字符串是否为日期字符串。您可能必须根据获取的数据类型编写该实用程序功能。

答案 4 :(得分:-1)

您可以编写一个接收对象结束的函数,每次返回fooDate。 此功能可以执行诸如广度优先搜索之类的操作,以在对象中找到此键。

使用此功能后,可以遍历对象对象数组并保存找到的最新对象。