【问题标题】:Filter and de dupe Javascript Objects based on it's property value根据属性值过滤和删除 Javascript 对象
【发布时间】:2021-03-02 12:01:13
【问题描述】:

基于“Platform”和“Title”,我需要对以下对象进行去重,得到最早的“DateFirstSeen”和最新的“DateLastSeen”。

输入对象:

[
 {
    "Platform": "Disney",
    "DateFirstSeen": {
      "Day": "15",
      "Month": "06",
      "Year": "2019"
    },
    "DateLastSeen": {
      "Day": "08",
      "Month": "02",
      "Year": "2021"
    },
    "Title": "Jojo Rabbit"
  },
  {
    "Platform": "Netflix",
    "DateFirstSeen": {
      "Month": "08",
      "Year": "2014"
    },
    "DateLastSeen": {
      "Month": "11",
      "Year": "2020"
    },
    "Title": "Stranger Things"
  },
  {
    "Platform": "Netflix",
    "DateFirstSeen": {
      "Day": "20",
      "Month": "08",
      "Year": "2014"
    },
    "DateLastSeen": {
      "Day": "02",
      "Month": "03",
      "Year": "2020"
    },
    "Title": "Stranger Things"
  },
  {
    "Platform": "Netflix",
    "DateFirstSeen": {
      "Month": "08",
      "Year": "2014"
    },
    "DateLastSeen": {
      "Month": "10",
      "Year": "2017"
    },
    "Title": "Stranger Things"
  },
  {
    "Platform": "Netflix",
    "DateFirstSeen": {
      "Month": "12",
      "Year": "2012"
    },
    "DateLastSeen": {
      "Day": "05",
      "Month": "01",
      "Year": "2017"
    },
    "Title": "Stranger Things"
  },
  {
    "Platform": "Hulu",
    "DateFirstSeen": {
      "Month": "05",
      "Year": "2010"
    },
    "DateLastSeen": {
      "Month": "12",
      "Year": "2016"
    },
    "Title": "Watchmen"
  },
  {
    "Platform": "Netflix",
    "DateFirstSeen": {
      "Day": "03",
      "Month": "04",
      "Year": "2015"
    },
    "DateLastSeen": {
      "Day": "03",
      "Month": "04",
      "Year": "2015"
    },
    "Title": "Stranger Things"
  },
  {
    "Platform": "AppleTV",
    "DateFirstSeen": {
      "Day": "20",
      "Month": "07",
      "Year": "2005"
    },
    "DateLastSeen": {
      "Month": "12",
      "Year": "2012"
    },
    "Title": "See"
  },
  {
    "Platform": "Hulu",
    "DateFirstSeen": {
      "Month": "01",
      "Year": "2012"
    },
    "DateLastSeen": {
      "Month": "01",
      "Year": "2012"
    },
    "Title": "Watchmen"
  },
  {
    "Platform": "AppleTV",
    "DateFirstSeen": {
      "Month": "09",
      "Year": "2003"
    },
    "DateLastSeen": {
      "Month": "02",
      "Year": "2009"
    },
    "Title": "Snoopy Show"
  },
  {
    "Platform": "AppleTV",
    "DateFirstSeen": {
      "Day": "01",
      "Month": "11",
      "Year": "2008"
    },
    "DateLastSeen": {
      "Day": "03",
      "Month": "12",
      "Year": "2008"
    },
    "Title": "See"
  },
  {
    "Platform": "AppleTV",
    "DateFirstSeen": {
      "Month": "09",
      "Year": "2008"
    },
    "DateLastSeen": {
      "Month": "09",
      "Year": "2008"
    },
    "Title": "See"
  },
  {
    "Platform": "AppleTV",
    "DateFirstSeen": {
      "Month": "06",
      "Year": "1999"
    },
    "DateLastSeen": {
      "Day": "31",
      "Month": "08",
      "Year": "2006"
    },
    "Title": "Snoopy Show"
  },
  {
    "Platform": "AppleTV",
    "DateFirstSeen": {
      "Day": "12",
      "Month": "08",
      "Year": "2006"
    },
    "DateLastSeen": {
      "Day": "12",
      "Month": "08",
      "Year": "2006"
    },
    "Title": "See"
  },
  {
    "Platform": "AppleTV",
    "DateFirstSeen": {
      "Day": "18",
      "Month": "06",
      "Year": "2006"
    },
    "DateLastSeen": {
      "Day": "18",
      "Month": "06",
      "Year": "2006"
    },
    "Title": "See"
  },
  {
    "Platform": "AppleTV",
    "DateFirstSeen": {
      "Month": "08",
      "Year": "2005"
    },
    "DateLastSeen": {
      "Month": "08",
      "Year": "2005"
    },
    "Title": "See"
  },
  {
    "Platform": "AppleTV",
    "DateFirstSeen": {
      "Day": "20",
      "Month": "07",
      "Year": "2005"
    },
    "DateLastSeen": {
      "Day": "20",
      "Month": "07",
      "Year": "2005"
    },
    "Title": "See"
  }
]

想要的输出是:

 [
  {
    "Platform": "Disney",
    "DateFirstSeen": {
      "Day": "15",
      "Month": "06",
      "Year": "2019"
    },
    "DateLastSeen": {
      "Day": "08",
      "Month": "02",
      "Year": "2021"
    },
    "Title": "Jojo Rabbit"
  },
  {
    "Platform": "Netflix",
    "DateFirstSeen": {
      "Month": "12",
      "Year": "2012"
    },
    "DateLastSeen": {
      "Month": "11",
      "Year": "2020"
    },
    "Title": "Stranger Things"
  },
  {
    "Platform": "Hulu",
    "DateFirstSeen": {
      "Month": "05",
      "Year": "2010"
    },
    "DateLastSeen": {
      "Month": "12",
      "Year": "2016"
    },
    "Title": "Watchmen"
  },
  {
    "Platform": "AppleTV",
    "DateFirstSeen": {
      "Day": "20",
      "Month": "07",
      "Year": "2005"
    },
    "DateLastSeen": {
      "Month": "12",
      "Year": "2012"
    },
    "Title": "See"
  },
  {
    "Platform": "AppleTV",
    "DateFirstSeen": {
      "Month": "06",
      "Year": "1999"
    },
    "DateLastSeen": {
      "Month": "02",
      "Year": "2009"
    },
    "Title": "Snoopy Show"
  }
]

我曾尝试使用下划线来获取所有唯一对象_.uniq,但这只会给我平台和标题的唯一值,但错过了 DateFirstSeen 和 DateLastSeen。

我想确保这些字段在过滤过程中不会丢失。

【问题讨论】:

  • 在js中可以使用Array.filter进行过滤,然后Array.sort根据日期,然后Array.reduce得到最小值或最大值(或者取排序后数组的第一个值)跨度>
  • 如果您根据_.uniq 显示您的(非工作)代码会有所帮助;展示您的研究成果,以便在此基础上提供答案。

标签: javascript arrays lodash underscore.js


【解决方案1】:

使用以下辅助函数,

function dayNumber(dateObj) {
    var day = _.get(dateObj, 'Day', 1),
        month = _.get(dateObj, 'Month'),
        year = _.get(dateObj, 'Year');
    return year * 372 + month * 31 + +day;
}

以下表达式将计算所需的输出:

_.chain(data).groupBy('Platform').map(function(platformData, platform) {
    return _.chain(platformData).groupBy('Title').map(function(titleData, title) {
        var titleChain = _.chain(titleData),
            firstSeen = titleChain.map('DateFirstSeen').min(dayNumber),
            lastSeen = titleChain.map('DateLastSeen').max(dayNumber);
        return {
            Platform: platform,
            Title: title,
            DateFirstSeen: firstSeen.value(),
            DateLastSeen: lastSeen.value()
        };
    }).value();
}).flatten().value();

让我们把它拆开。

function dayNumber(dateObj) {

我们首先编写一个函数,该函数接受一个日期对象,例如{ "Day": "20", "Month": "07", "Year": "2005" },并返回一个数字。我们需要这个才能应用_.min_.max,它们可以分别计算集合的最小值和最大值。

    var day = _.get(dateObj, 'Day', 1),

dayNumber 函数中,我们继续从dateObj 中取出部分。 _.get 函数让我们可以指定一个备用值(在本例中为 1),这很方便,因为并非所有日期对象都有 Day 属性。

        month = _.get(dateObj, 'Month'),
        year = _.get(dateObj, 'Year');
    return year * 372 + month * 31 + +day;
}

通过这些部分,我们计算了自西历开始以来的天数的近似值。该值显然不准确,因为我们假设每个月有 31 天,但它仍然会正确获取日期的相对顺序。请注意,我们使用符号+day 来强制Day 属性为数字。年份和月份是由我们将它们乘以一个数字来强制转换的。

有了dayNumber 函数,我们可以继续讨论大表达式。

_.chain(data)

我们使用_.chain 以便我们可以连续应用几个Underscore 函数,每个Underscore 函数都对前一个Underscore 函数的结果进行操作。我们将在表达式的其余部分中再看到几次。

.groupBy('Platform')

仍然在第一行,我们首先按Platform 属性对数据进行分组。这将创建一个对象,其中每个键是一个平台名称,对应的值是来自该平台的数据的子集:

{
    Disney: [...],
    Netflix: [...],
    Hulu: [...],
    AppleTV: [...]
}

这个中间结果立即链接到下一个下划线函数:

.map(function(platformData, platform) {

对于每一对平台名称及其对应的数据,我们将计算某种结果,然后将所有结果返回到一个数组中(参见_.map)。在下一行,我们发现自己在计算这样一个结果的函数中。

    return _.chain(platformData).groupBy('Title').map(function(titleData, title) {

这一行看起来与上一行惊人地相似。我们只对单个平台的数据进行分组,而不是所有数据,并按标题对它们进行分组。前面有return语句,表示这条链的最终结果也将是整个平台的计算结果。

        var titleChain = _.chain(titleData),

我们为单个标题的数据子集制作了另一个链。我们给这个链命名(titleChain),因为我们将使用它两次。

            firstSeen = titleChain.map('DateFirstSeen').min(dayNumber),
            lastSeen = titleChain.map('DateLastSeen').max(dayNumber);

我们终于开始做生意了!首先,我们从titleData 中提取所有DateFirstSeen 属性,然后我们使用_.min 和我们的dayNumber 辅助函数提取最早的属性。同样,我们使用_.max 找到最新的DateLastSeen

        return {
            Platform: platform,
            Title: title,
            DateFirstSeen: firstSeen.value(),
            DateLastSeen: lastSeen.value()
        };

我们拥有关于这个单一标题的所有信息,因此我们将数据重新组合成一个新对象。我们使用_().value 将结果从我们的链中取出。

    }).value();

我们关闭计算单个标题的结果的函数以及对我们传递此函数的_.map 的调用。我们也结束了为整个平台计算结果的链并取其.value()。这是一个标题对象数组。

}).flatten().value();

最后,我们关闭整个表达式。我们插入对_.flatten 的调用,因为否则我们将拥有一个数组数组,而不仅仅是一个平面数组。

注意:如果我们保证每个标题只出现在一个平台上,我们可以跳过按平台分组,最后不需要flatten。在这种情况下,代码更简单,也更快一点:

_.chain(data).groupBy('Title').map(function(titleData, title) {
    var titleChain = _.chain(titleData),
        firstSeen = titleChain.map('DateFirstSeen').min(formatAsDate),
        lastSeen = titleChain.map('DateLastSeen').max(formatAsDate);
    return {
        Platform: _.first(titleData).Platform,
        Title: title,
        DateFirstSeen: firstSeen.value(),
        DateLastSeen: lastSeen.value()
    };
}).value();

由于我们不再按平台分组,我们不再有要关闭的platform 参数,因此我们使用_.first 来获取平台名称。通常,您不能假设数组有第一个元素(因为数组可能为空),但在这里它是安全的,因为groupBy 从不产生空组。

这个更简单的表达式恰好适用于您的示例数据,但一般来说,假设每个标题对于单个平台都是唯一的可能并不安全。您的里程可能会有所不同。

【讨论】:

    【解决方案2】:

        const data =  [
         {
            "Platform": "Disney",
            "DateFirstSeen": {
              "Day": "15",
              "Month": "06",
              "Year": "2019"
            },
            "DateLastSeen": {
              "Day": "08",
              "Month": "02",
              "Year": "2021"
            },
            "Title": "Jojo Rabbit"
          },
          {
            "Platform": "Netflix",
            "DateFirstSeen": {
              "Month": "08",
              "Year": "2014"
            },
            "DateLastSeen": {
              "Month": "11",
              "Year": "2020"
            },
            "Title": "Stranger Things"
          },
          {
            "Platform": "Netflix",
            "DateFirstSeen": {
              "Day": "20",
              "Month": "08",
              "Year": "2014"
            },
            "DateLastSeen": {
              "Day": "02",
              "Month": "03",
              "Year": "2020"
            },
            "Title": "Stranger Things"
          },
          {
            "Platform": "Netflix",
            "DateFirstSeen": {
              "Month": "08",
              "Year": "2014"
            },
            "DateLastSeen": {
              "Month": "10",
              "Year": "2017"
            },
            "Title": "Stranger Things"
          },
          {
            "Platform": "Netflix",
            "DateFirstSeen": {
              "Month": "12",
              "Year": "2012"
            },
            "DateLastSeen": {
              "Day": "05",
              "Month": "01",
              "Year": "2017"
            },
            "Title": "Stranger Things"
          },
          {
            "Platform": "Hulu",
            "DateFirstSeen": {
              "Month": "05",
              "Year": "2010"
            },
            "DateLastSeen": {
              "Month": "12",
              "Year": "2016"
            },
            "Title": "Watchmen"
          },
          {
            "Platform": "Netflix",
            "DateFirstSeen": {
              "Day": "03",
              "Month": "04",
              "Year": "2015"
            },
            "DateLastSeen": {
              "Day": "03",
              "Month": "04",
              "Year": "2015"
            },
            "Title": "Stranger Things"
          },
          {
            "Platform": "AppleTV",
            "DateFirstSeen": {
              "Day": "20",
              "Month": "07",
              "Year": "2005"
            },
            "DateLastSeen": {
              "Month": "12",
              "Year": "2012"
            },
            "Title": "See"
          },
          {
            "Platform": "Hulu",
            "DateFirstSeen": {
              "Month": "01",
              "Year": "2012"
            },
            "DateLastSeen": {
              "Month": "01",
              "Year": "2012"
            },
            "Title": "Watchmen"
          },
          {
            "Platform": "AppleTV",
            "DateFirstSeen": {
              "Month": "09",
              "Year": "2003"
            },
            "DateLastSeen": {
              "Month": "02",
              "Year": "2009"
            },
            "Title": "Snoopy Show"
          },
          {
            "Platform": "AppleTV",
            "DateFirstSeen": {
              "Day": "01",
              "Month": "11",
              "Year": "2008"
            },
            "DateLastSeen": {
              "Day": "03",
              "Month": "12",
              "Year": "2008"
            },
            "Title": "See"
          },
          {
            "Platform": "AppleTV",
            "DateFirstSeen": {
              "Month": "09",
              "Year": "2008"
            },
            "DateLastSeen": {
              "Month": "09",
              "Year": "2008"
            },
            "Title": "See"
          },
          {
            "Platform": "AppleTV",
            "DateFirstSeen": {
              "Month": "06",
              "Year": "1999"
            },
            "DateLastSeen": {
              "Day": "31",
              "Month": "08",
              "Year": "2006"
            },
            "Title": "Snoopy Show"
          },
          {
            "Platform": "AppleTV",
            "DateFirstSeen": {
              "Day": "12",
              "Month": "08",
              "Year": "2006"
            },
            "DateLastSeen": {
              "Day": "12",
              "Month": "08",
              "Year": "2006"
            },
            "Title": "See"
          },
          {
            "Platform": "AppleTV",
            "DateFirstSeen": {
              "Day": "18",
              "Month": "06",
              "Year": "2006"
            },
            "DateLastSeen": {
              "Day": "18",
              "Month": "06",
              "Year": "2006"
            },
            "Title": "See"
          },
          {
            "Platform": "AppleTV",
            "DateFirstSeen": {
              "Month": "08",
              "Year": "2005"
            },
            "DateLastSeen": {
              "Month": "08",
              "Year": "2005"
            },
            "Title": "See"
          },
          {
            "Platform": "AppleTV",
            "DateFirstSeen": {
              "Day": "20",
              "Month": "07",
              "Year": "2005"
            },
            "DateLastSeen": {
              "Day": "20",
              "Month": "07",
              "Year": "2005"
            },
            "Title": "See"
          }
        ];
        const result = data.reduce((a,it)=>{
         const index = a.map(i=>i.Title).indexOf(it.Title);
         if(index===-1){
           a.push(it);
         }else{
           if(a[index].DateLastSeen.Year < it.DateLastSeen.Year || a[index].DateLastSeen.Month < it.DateLastSeen.Month){
           a[index].DateLastSeen = it.DateLastSeen;
            }
         }
        return a;
        },[])
        console.log(result);

    【讨论】:

      猜你喜欢
      • 2017-03-19
      • 2015-05-30
      • 2021-10-19
      • 2018-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-25
      • 2016-05-12
      相关资源
      最近更新 更多