简体   繁体   English

转换对象结构数组

[英]Transform an array of objects structure

At the moment I am working with an api that returns an array of objects that looks like目前我正在使用一个 api,它返回一个看起来像的对象数组

[{match_id: "255232",
country_id: "41",
country_name: "England",
league_id: "152",
league_name: "National League",
match_date: "2020-01-01",
match_status: "",
match_time: "16:00"},
{match_id: "255232",
    country_id: "41",
    country_name: "Italy",
    league_id: "152",
    league_name: "Serie a",
    match_date: "2020-01-01",
    match_status: "",
    match_time: "16:00"},
{match_id: "255232",
        country_id: "41",
        country_name: "Italy",
        league_id: "153",
        league_name: "Serie b",
        match_date: "2020-01-01",
        match_status: "",
        match_time: "16:00"},...
    ...

]

I would like to transform it in a way that ends up looking like:我想以一种最终看起来像这样的方式来改变它:

const transformed = [
{
    country_name: "England",
    entries: [
        {league_name: "National League",
        events: [{},{}]}
    ]

},
{country_name: "Italy",
entries: [
    {
        league_name: "Serie a",
        events: [{},{}]
    },
    {
        league_name: "Serie b",
        events: [{},{}]
    }...
]]

I have tried to use .reduce but did not end up with expected output and I end up with just a reverted structure.我曾尝试使用 .reduce 但最终没有得到预期的输出,我最终得到的只是一个恢复结构。 Basically what I need is to catalogue by country_name first and league_name second.基本上我需要的是首先按 country_name 和 League_name 进行编目。

Obviously the data is dynamic and the names of countries/leagues change often.显然数据是动态的,国家/联盟的名称经常变化。

I've provided two solutions - one that returns an object keyed by country and league names (this would be my preference / recommendation) and one that extends the first solution to return the shape you requested.我提供了两种解决方案 - 一种返回以国家和联赛名称为键的对象(这将是我的偏好/推荐),另一种扩展第一个解决方案以返回您请求的形状。

This first snippet transforms your input into an object keyed by country and league names:第一个片段将您的输入转换为以国家和联赛名称为键的对象:

 const data = [{match_id: "255232",country_id: "41",country_name: "England",league_id: "152",league_name: "National League",match_date: "2020-01-01",match_status: "",match_time: "16:00"},{match_id: "255233",country_id: "41",country_name: "Italy",league_id: "152",league_name: "Serie a",match_date: "2020-01-01",match_status: "",match_time: "16:00"},{match_id: "255234",country_id: "41",country_name: "Italy",league_id: "152",league_name: "Serie a",match_date: "2020-01-01",match_status: "",match_time: "16:00"}] const transformed = data.reduce( (acc, { country_name, league_name, ...match }) => { acc[country_name] = acc[country_name] || {} acc[country_name][league_name] = acc[country_name][league_name] || [] acc[country_name][league_name].push(match) return acc }, {} ) console.log(transformed)

This second snippet extends the first one, returning the shape you requested, originally:第二个片段扩展了第一个片段,返回您请求的形状,最初:

 const data = [{match_id: "255232",country_id: "41",country_name: "England",league_id: "152",league_name: "National League",match_date: "2020-01-01",match_status: "",match_time: "16:00"},{match_id: "255233",country_id: "41",country_name: "Italy",league_id: "152",league_name: "Serie a",match_date: "2020-01-01",match_status: "",match_time: "16:00"},{match_id: "255234",country_id: "41",country_name: "Italy",league_id: "152",league_name: "Serie a",match_date: "2020-01-01",match_status: "",match_time: "16:00"}] const tmp = data.reduce( (acc, { country_name, league_name, ...match }) => { acc[country_name] = acc[country_name] || {} acc[country_name][league_name] = acc[country_name][league_name] || [] acc[country_name][league_name].push(match) return acc }, {} ) const transformed = Object.entries(tmp).map( ([country_name, leagues]) => ({ country_name, entries: Object.entries(leagues).map( ([league_name, events]) => ({ league_name, events }) ) }) ) console.log(transformed)

There are a few reasons I prefer the keyed object to the array:有几个原因我更喜欢键控对象而不是数组:

  1. There's less nesting in the keyed object, and it will be smaller when stringified.键控对象中的嵌套更少,字符串化时它会更小。
  2. It's easier to get all entries for a country and/or a country + league from the object (eg transformed.England["National League"] ).从对象中获取一个国家和/或一个国家+联赛的所有条目(例如, transformed.England["National League"] )更容易。
  3. It's less work to generate the object.生成对象的工作较少。
  4. One entry per country means the 'correct' data structure is Map (correct in most but not necessarily all cases).每个国家一个条目意味着“正确”的数据结构是Map (在大多数情况下但不一定在所有情况下都是正确的)。 The object is a better approximation of a Map than the array.对象是比数组更好的Map近似。

I use (and teach) a similar technique with success in redux .我在redux使用(并教授)了类似的技术并取得了成功。 Rather than just storing an array of objects returned by an API, I also store an object based on the array that is indexed by the objects' ids.我不仅存储 API 返回的对象数组,还存储基于对象 ID 索引的数组的对象。 It's much easier to work with this object than it is with the raw array in many cases:在许多情况下,使用这个对象比使用原始数组容易得多:

const arr = [{ id: 'a', foo: 'bar' }, { id: 'b', foo: 'baz' }]
const obj = { a: { foo: 'bar' }, b: { foo: 'baz' } }

Here's a quick snippet showing how to go from the object to your desired React output, if it turns out the object is a useful intermediate data structure:这是一个快速片段,显示了如何从对象转到所需的 React 输出,如果结果证明该对象是一个有用的中间数据结构:

 const { Fragment } = React const data = [{match_id: "255232",country_id: "41",country_name: "England",league_id: "152",league_name: "National League",match_date: "2020-01-01",match_status: "",match_time: "16:00"},{match_id: "255233",country_id: "41",country_name: "Italy",league_id: "152",league_name: "Serie a",match_date: "2020-01-01",match_status: "",match_time: "16:00"},{match_id: "255234",country_id: "41",country_name: "Italy",league_id: "152",league_name: "Serie a",match_date: "2020-01-01",match_status: "",match_time: "16:00"}] const transformed = data.reduce( (acc, { country_name, league_name, ...match }) => { acc[country_name] = acc[country_name] || {} acc[country_name][league_name] = acc[country_name][league_name] || [] acc[country_name][league_name].push(match) return acc }, {} ) const League = ({ league, matches }) => ( <Fragment> <h2>{league}</h2> {matches.map(({ match_id }) => (<p>{match_id}</p>))} </Fragment> ) ReactDOM.render( Object.entries(transformed).map(([country, leagues]) => ( <Fragment> <h1>{country}</h1> {Object.entries(leagues).map(([league, matches]) => ( <League league={league} matches={matches} /> ))} </Fragment> )), document.body )
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id="react"></div>

This seems like a use case for a for loop, that creates or push es each "result" into the appropriate Country index, then uses similar logic to organize the matches for each country by League.这似乎是for循环的一个用例,它将每个“结果”创建或push送到适当的国家/地区索引中,然后使用类似的逻辑按联赛组织每个国家/地区的比赛。 The code below is pretty verbose-- if you use a utility library like Lodash, you can achieve some of this much more succinctly via something like groupBy (although, that would produce an object instead of an array).下面的代码非常冗长——如果你使用像 Lodash 这样的实用程序库,你可以通过像groupBy这样的东西更简洁地实现其中的一些(尽管,这会产生一个对象而不是一个数组)。

const results = [/* ...your original data... */];
const resultsByCountry = [];

// The first loop just indexes the matches by Country:
for (let r = 0; r < results.length; r++) {
  let result = results[r];
  let existingCountryIndex = resultsByCountry.findIndex((country) => country.country_name == result.country_name);

  // Temporarily, we're just stashing the matches flat, here. We'll take another pass to group them into leagues, once we know they're all sorted into the right countries:
  if (existingCountryIndex > -1) {
    resultsByCountry[existingCountryIndex].entries.push(result);
  } else {
    resultsByCountry.push({
      country_name: result.country_name,
      entries: [result]
    });
  }
}


// The second loop organizes the matches into leagues, by a very similar means:
for (let c = 0; c < resultsByCountry.length; c++) {
  const country = resultsByCountry[c]
  let matches = country.entries;
  let matchesByLeague = [];

  for (let m = 0; m < matches.length; m++) {
    let match = matches[m];
    let existingLeagueIndex = matchesByLeague.findIndex((league) => league.league_name == match.league_name);

    if (existingLeagueIndex > -1) {
      matchesByLeague[existingLeagueIndex].events.push(match);
    } else {
      matchesByLeague.push({
        league_name: match.league_name,
        events: [match]
      });
    }
  }

  // Overwrite the old `entries` key with the grouped array:
  country.entries = matchesByLeague;
}

console.log(resultsByCountry);

Again, this is pretty crude.同样,这是非常粗糙的。 There is probably a way to implement it with map and reduce , but because the data structure is unusual, and the question specifically asked about transforming from an array to an array (not an object), I've provided this as a simple + explicit answer.可能有一种方法可以使用mapreduce来实现它,但是由于数据结构不寻常,并且特别询问了有关从数组转换为数组(不是对象)的问题,因此我将其作为简单+显式提供回答。

You could store all information for building nested group/value pairs and take an array for all keys for the final objects and reduce the array by reducing the group array.您可以存储用于构建嵌套组/值对的所有信息,并为最终对象的所有键获取一个数组,并通过减少组数组来减少数组。

 var data = [{ match_id: "255232", country_id: "41", country_name: "England", league_id: "152", league_name: "National League", match_date: "2020-01-01", match_status: "", match_time: "16:00" }, { match_id: "255232", country_id: "41", country_name: "Italy", league_id: "152", league_name: "Serie a", match_date: "2020-01-01", match_status: "", match_time: "16:00" }, { match_id: "255232", country_id: "41", country_name: "Italy", league_id: "153", league_name: "Serie b", match_date: "2020-01-01", match_status: "", match_time: "16:00" }], groups = [['country_name', 'entries'], ['league_name', 'events']], values = ['match_date', 'match_status', 'match_time'], result = data.reduce((r, o) => { groups .reduce((group, [key, values]) => { var temp = group.find(q => q[key] === o[key]); if (!temp) group.push(temp = { [key]: o[key], [values]: [] }); return temp[values]; }, r) .push(Object.assign(...values.map(k => ({ [k]: o[k] })))); return r; }, []); console.log(result);
 .as-console-wrapper { max-height: 100% !important; top: 0; }

const map1 = {};
const list = matches.map(match => {
  if (!map1[match.country_name]) {
    map1[match.country_name] = {};
  }
  map1[match.country_name]["entries"] =
    map1[match.country_name]["entries"] &&
    map1[match.country_name]["entries"].length > 0
      ? map1[match.country_name]["entries"].concat(match)
      : [match];
});

const result = [];
Object.keys(map1).forEach(country => {
  const m = {};
  m["country_name"] = country;
  m["entries"] = [];
  const entries = map1[country]["entries"];

  entries.forEach(entry => {
    m["entries"].push({
      league_name: entry.league_name,
      events: entry
    });
  });

  result.push(m);
});
// result has the required info.

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

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