Создание массива пользовательских объектов из вложенной структуры данных - PullRequest
0 голосов
/ 27 мая 2018

Я работаю над проектом, который требует от меня массирования некоторых данных API (показано ниже во фрагменте как «apiData»).Структура данных, которая мне в конечном итоге нужна для библиотеки графиков, которую я использую (Recharts), такова:

[ 
 { date: '2018-04-24', TSLA: 283.37, AAPL: 250.01 },
 { date: '2018-04-25', AAPL: 320.34 } 
]

Я собрал нижеприведенную функцию, и она работает достаточно хорошо, но у меня проблемы с получениемвсе данные, чтобы показать, даже если нет совпадения между датами.В приведенном ниже примере вы заметите, что объект на дату «2018-04-23» в apiData исключен.В идеале финальный ds должен выглядеть следующим образом:

[ 
 { date: '2018-04-23', TSLA: 285.12 }
 { date: '2018-04-24', TSLA: 283.37, AAPL: 250.01 },
 { date: '2018-04-25', AAPL: 320.34 } 
]

Кроме того, возможно, есть более эффективный способ сделать это, но я какое-то время взламывал и не видел лучшего решения на данный момент,Например, forEach не идеален по мере роста набора данных, что будет происходить, когда мне нужно будет строить длинные периоды данных.

Итак, мои вопросы: 1) Как я могу сделатьчто объекты, совпадающие по дате, объединены, а объекты, которые еще не включены, и 2) какой более эффективный способ выполнить эту операцию?

Если у кого-либо есть какие-либо замечания или замечания по поводу моего подхода и как я могу это сделатьулучшить его, я был бы очень признателен.

Вот ссылка на repl , если это более удобно, чем фрагмент кода ниже.

formatChartData = (data) => {
  const chartData = data
    .reduce((arr, stock) => {
      const stockArr = stock.chart.map((item) => {
        let chartObj = {};

        chartObj.date = item.date;
        chartObj[stock.quote.symbol] = item.close;

        if (arr.length > 0) {
          arr.forEach((arrItem) => {
            if (arrItem.date === item.date) {
              chartObj = { ...arrItem, ...chartObj };
            }
          });
        }
        return chartObj;
      });

      return stockArr;
    }, []);

  console.log(chartData)
}

const apiData = [
  {
    chart: [
      {
        date: "2018-04-23",
        open: 291.29,
        close: 285.12,
      },
      {
        date: "2018-04-24",
        open: 291.29,
        close: 283.37,
      },
    ],
    news: [],
    quote: {
      symbol: "TSLA"
    },
  },
  {
    chart: [
      {
        date: "2018-04-24",
        open: 200.29,
        close: 250.01,
      },
      {
        date: "2018-04-25",
        open: 290.20,
        close: 320.34,
      },
    ],
    news: [],
    quote: {
      symbol: "AAPL"
    },
  }
]

formatChartData(apiData)

РЕДАКТИРОВАТЬ: В итоге я решил использовать решение charlietfl с внутренним forEach, поскольку мне было легче читать, чем два метода Reduce.Последняя функция выглядит так:

 const chartData = data
  .reduce((map, stock) => {
    stock.chart.forEach((chart) => {
      const chartObj = map.get(chart.date) || { date: chart.date };
      chartObj[stock.quote.symbol] = chart.close;
      map.set(chart.date, chartObj);
    });
    return map;
  }, new Map());`

Ответы [ 3 ]

0 голосов
/ 27 мая 2018

Просто исправьте только ваш код, иначе вместо карты следует использовать сокращение, также для диаграмм.

formatChartData = (data) => {
  const chartData = data
    .reduce((arr, stock) => {
      const stockArr = stock.chart.map((item) => {
        let chartObj = {};

        chartObj.date = item.date;
        chartObj[stock.quote.symbol] = item.close;

        if (arr.length > 0) {
          arr.forEach((arrItem, i) => {
            if (arrItem.date === item.date) {
              chartObj = { ...arrItem, ...chartObj };
              delete(arr[i]);
            }
          });
        }
        return chartObj;
      });

      return [...arr, ...stockArr].filter(e=>!!e); //to remove undefined elements left by delete above.
    }, []);

  console.log(chartData)
}

const apiData = [
  {
    chart: [
      {
        date: "2018-04-23",
        open: 291.29,
        close: 285.12,
      },
      {
        date: "2018-04-24",
        open: 291.29,
        close: 283.37,
      },
    ],
    news: [],
    quote: {
      symbol: "TSLA"
    },
  },
  {
    chart: [
      {
        date: "2018-04-24",
        open: 200.29,
        close: 250.01,
      },
      {
        date: "2018-04-25",
        open: 290.20,
        close: 320.34,
      },
    ],
    news: [],
    quote: {
      symbol: "AAPL"
    },
  }
]

formatChartData(apiData)
0 голосов
/ 27 мая 2018

Проблема в том, что ваша функция сокращения работает для каждого элемента в массиве данных.Когда он запускается на первом элементе в массиве данных, он возвращает:

[
    { date: '2018-04-23', TSLA: 285.12 },
    { date: '2018-04-24', TSLA: 283.37 }
]

Когда он запускается на втором элементе в массиве, он возвращает это:

[
    { date: '2018-04-24', TSLA: 283.37, AAPL: 250.01 },
    { date: '2018-04-25', AAPL: 320.34 } 
]

Это потому, что когдаредукция выполняется для последнего элемента массива, который возвращает результат этого элемента.Вы только объединяете элементы из переменной аккумулятора "arr", если их дата также есть в текущем элементе массива.Так как 2018-04-23 находится в первом, но не во втором, оно не добавляется.Я добавил две вещи в ваш код.Во-первых, если текущая дата в цикле находится в переменной-накопителе «arr», я удаляю ее из «arr» после того, как она объединена. Второе изменение - после каждого цикла .reduce в «arr» остаются еще несколько дат, которыенет в текущем "stockArr".Чтобы справиться с этим, я объединяю «arr» и «stockArr», что даст вам то, что вы ищете.

formatChartData = (data) => {
  const chartData = data
    .reduce((arr, stock) => {
      const stockArr = stock.chart.map((item) => {
        let chartObj = {};

        chartObj.date = item.date;
        chartObj[stock.quote.symbol] = item.close;
        
        if (arr.length > 0) {
          arr.forEach((arrItem, index) => {
            if (arrItem.date === item.date) {
              chartObj = { ...arrItem, ...chartObj };
              arr.splice(index, 1);
            }
          });
        }
        return chartObj;
      });
      return [...arr, ...stockArr];
    }, []);

  console.log(chartData)
}

const data = [
  {
    chart: [
      {
        date: "2018-04-23",
        open: 291.29,
        close: 285.12,
      },
      {
        date: "2018-04-24",
        open: 291.29,
        close: 283.37,
      },
    ],
    news: [],
    quote: {
      symbol: "TSLA"
    },
  },
  {
    chart: [
      {
        date: "2018-04-24",
        open: 200.29,
        close: 250.01,
      },
      {
        date: "2018-04-25",
        open: 290.20,
        close: 320.34,
      },
    ],
    news: [],
    quote: {
      symbol: "AAPL"
    },
  }
]

formatChartData(data)
0 голосов
/ 27 мая 2018

Более чистым способом, чем обходить каждый накопленный новый массив каждый раз для поиска даты, является использование одного главного объекта с датами в качестве ключей

После этого я использую reduce() для возврата Map (также может быть литералом объекта) с использованием дат в качестве ключей, а затем преобразовать значения карты итеративно в массив для получения окончательных результатов

const dateMap = apiData.reduce((map,stock)=>{ 
   return stock.chart.reduce((_, chartItem)=>{
      // get stored object for this date, or create new object
      const dayObj = map.get(chartItem.date) || {date: chartItem.date};
      dayObj[stock.quote.symbol] = chartItem.close;
      // store the object in map again using date as key
      return map.set(chartItem.date, dayObj);
   },map);  
}, new Map)

const res = [...dateMap.values()];

console.log(res)
.as-console-wrapper {max-height: 100%!important;}
<script>
const apiData = [
  {
    chart: [
      {
        date: "2018-04-23",
        open: 291.29,
        close: 285.12,
      },
      {
        date: "2018-04-24",
        open: 291.29,
        close: 283.37,
      },
    ],
    news: [],
    quote: {
      symbol: "TSLA"
    },
  },
  {
    chart: [
      {
        date: "2018-04-24",
        open: 200.29,
        close: 250.01,
      },
      {
        date: "2018-04-25",
        open: 290.20,
        close: 320.34,
      },
    ],
    news: [],
    quote: {
      symbol: "AAPL"
    },
  }
]


</script>
...