使用reduce将数组转换为对象数组

24
optimizedRoute = ['Bengaluru', 'Salem', 'Erode', 'Tiruppur', 'Coimbatore']

result = [
  {start: bengaluru, end: salem},
  {start: salem, end: erode},
  {start: erode, end: tiruppur},
  {start: tiruppur, end: coimbatore},
]

我希望将 optimizedRoute 转换为结果。我希望使用ES6的.reduce()来实现这一点。以下是我的尝试:

const r = optimizedRoute.reduce((places, place, i) => {
  const result: any = [];
  places = []
  places.push({
    startPlace: place,
    endPlace: place
  });
  // result.push ({ startplace, endplace, seats: 4 });
  // console.log(result);
  return places;
}, {});
console.log(r)
10个回答

14
您可以使用reduce来获取路由的起始和结束部分,并将结束部分返回作为下一个起始部分。
getParts = a => (                   // take a as array and return an IIFE
    r => (                          // with an initialized result array
        a.reduce((start, end) => (  // reduce array by taking two values
            r.push({ start, end }), // push short hand properties
            end                     // and take the last value as start value for next loop
        )),
        r                           // finally return result
    )
)([]);                              // call IIFE with empty array

const getParts = a => (r => (a.reduce((start, end) => (r.push({ start, end }), end)), r))([]);

var optimizedRoute = ['Bengaluru', 'Salem', 'Erode', 'Tiruppur', 'Coimbatore']

console.log(getParts(optimizedRoute));
.as-console-wrapper { max-height: 100% !important; top: 0; }


@编辑 Grégory NEUT 添加说明


// Two thing to know first :

// When no initial value is provided,
// Array.reduce takes the index 0 as first value and start to loop at index 1

// Doing (x, y, z)
// Will execute the code x, y and z

// Equivalent to :

// x;
// y;
// z;

let ex = 0;

console.log((ex = 2, ex = 5, ex = 3));

// So about the code

const getParts = (a) => {
  // We are creating a new function here so we can have an array where to
  // push data to
  const func = (r) => {
    // Because there is no initial value
    //
    // Start will be the value at index 0 of the array
    // The loop is gonna start at index 1 of the array
    a.reduce((start, end) => {
      console.log(start, end);

      r.push({
        start,
        end,
      });

      return end;
    });

    return r;
  };

  return func([]);
};

// Equivalent
const getPartsEquivalent = (a) => {
  const r = [];

  // Because there is no initial value
  //
  // Start will be the value at index 0 of the array
  // The loop is gonna start at index 1 of the array
  a.reduce((start, end) => {
    console.log(start, end);

    r.push({
      start,
      end,
    });

    return end;
  });

  return r;
};

var optimizedRoute = ['Bengaluru', 'Salem', 'Erode', 'Tiruppur', 'Coimbatore']

console.log(getPartsEquivalent(optimizedRoute));
.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}


4
这很让人印象深刻,但难以获得! - sjahan
1
尽管这可能是一个(好的)解决方案,但考虑到OP的技能水平,我认为这对于他们来说太难理解了。 - Giorgi Moniava
妮娜,你能否添加更多细节来说明这个棒极了的一行代码的步骤吗? - sjahan
@Nina Scholz 我不明白为什么第一个推入的值不是 { start: undefined, end: 'Bengaluru',} 。这是否与 reduce 在没有初始值时忽略第一个值有关? - Orelsanpls
1
@GrégoryNEUT,对的,如果reduce没有初始值,那么数组中的前两个元素将作为累加器和当前项。索引从1开始计数。例如,您可以尝试这个:[4, 5, 6, 7].reduce((a, b, i) => (console.log(i, a, b), b)); - Nina Scholz
显示剩余8条评论

12
另一种方法是将map方法与slice结合使用。对于map函数,您需要将一个回调函数作为参数传递,该函数将应用于给定数组中的每个项。

optimizedRoute = ['Bengaluru', 'Salem', 'Erode', 'Tiruppur', 'Coimbatore']
var result = optimizedRoute
                .slice(0, -1)
                .map((item, index) => ({start : item, end : optimizedRoute[index + 1]}));
console.log(result);


11

对于“使用reduce”的要求,我并不太理解,因为使用循环的相应代码可以立即阅读且不需要解释:

const optimizedRoute = ['Bengaluru', 'Salem', 'Erode', 'Tiruppur', 'Coimbatore'];
const result = new Array(optimizedRoute.length - 1);

for (let i = 0; i < result.length; ++i) {
  result[i] = {
    start: optimizedRoute[i],
    end: optimizedRoute[i + 1]
  };
}

console.log(result)

有时候做一些聪明的事情很有趣,但与此相比,有些答案非常复杂!


5
这里有一个使用reduce的例子。不过我不确定这是最自然的方法!
在这种情况下,使用reduce可能有些过度了(但这只是我的意见)。如果我需要使用索引,我会选择简单的for循环。

const optimizedRoute = ['Bengaluru', 'Salem', 'Erode', 'Tiruppur', 'Coimbatore'];
let startCity;
const result = optimizedRoute.reduce((acc, city) => {
  if(startCity) {
    acc.push({start: startCity, end: city});
  }
  startCity = city;
  return acc;
}, []);

console.log(result);


3

既然你要求使用reduce,这是一种实现方式:

let optimizedRoute = ['Bengaluru', 'Salem', 'Erode', 'Tiruppur', 'Coimbatore']
   
let res = optimizedRoute.reduce((accum, item, i)=>{
   if(i == optimizedRoute.length - 1) 
      return accum;
   accum.push({start: item, end: optimizedRoute[i+1]})
   return accum;
}, [])

console.log(res);


3

reduce在这里并不适用,因为你并不是试图将数组减少为单个值。

在完美的世界里,我们会有一个多维数组map版本,通常被称为zip,我们可以像这样使用

const result = zipWith(optimisedRoute.slice(0, -1),
                       optimisedRoute.slice(1),
                       (start, end) => ({start, end}));

但是在JavaScript中没有这种方法。最好的替代方法是使用Array.from映射路由中一系列索引:

const result = Array.from({length: optimisedRoute.length - 1}, (_, index) => {
     const start = optimisedRoute[index];
     const end = optimisedRoute[index + 1];
     return {start, end};
});

这种方法与Mihai Alexandru-Ionut和Tom Fenech所做的非常相似,你也应该给他们的答案点赞! - Bergi
@saila 抱歉,已修复。流程非常简单,Array.from({length: …}, …) 是一个已知的技巧,基本上等同于 range(0, …).map(…) - Bergi
@ Bergi 根据 O 表示法,哪个是最好的? - kartik
@saila 我认为这里提出的所有解决方案都具有线性运行时间。 - Bergi

2
以下代码使用了展开运算符三元运算符数组.reduce方法。

const optimizedRoute = [
  'Bengaluru',
  'Salem',
  'Erode',
  'Tiruppur',
  'Coimbatore',
];

// Look if we are at dealing with the last value or not
// If we do only return the constructed array
// If we don't, add a new value into the constructed array.

// tmp is the array we are constructing
// x the actual loop item
// xi the index of the item
const lastItemIndex = optimizedRoute.length - 1;

const ret = optimizedRoute.reduce((tmp, x, xi) => xi !== lastItemIndex ? [
  ...tmp,

  {
    start: x,
 
    // We access to the next item using the position of
    // the current item (xi)
    end: optimizedRoute[xi + 1],
  },
] : tmp, []);

console.log(ret);


2

我简化了Nina Scholz的回答。根据Nina的想法,使用reduce函数获取路线的起始和结束部分,并将结束部分作为下一个起始部分返回。

getParts = a => {
  const result = [];
    a.reduce((start, end) => {
      result.push({ start, end });
      return end;
    });
    return result;
};
var optimizedRoute = ['Bengaluru', 'Salem', 'Erode', 'Tiruppur', 'Coimbatore'];
console.log(this.getParts(optimizedRoute));

0

我更注重可读性,而不仅仅是解决问题的简短代码

optimizedRoute.reduce((routes, city, index) => {
  const firstCity = index === 0;
  const lastCity = index === optimizedRoute.length - 1;
  if (!firstCity) {
    routes.last().end = city;
  }
  if (!lastCity) {
    routes.push({ start: city });
  }
  return routes;
}, []);

此外,那个解决方案虽然更短,但可读性很差(至少对我来说是这样),可以尝试:

optimizedRoute.reduce((routes, city) => {
  routes.last().start = city;
  routes.push({ end: city });
  return routes;
}, [{}]).slice(1, -1);

至于last(),这是我通常为了可读性而使用的函数:

Array.prototype.last = function() { 
  return this[this.length - 1] 
}

-1

如果有人在寻找解决方案,可以使用ReduceRight。

optimizedRoute.reduceRight((acc, d, i, arr) => 
             i == 0 
                ? acc 
                : [{ start: arr[i -1], end: d }, ...acc]  
            , [])

请解释一下为什么要在这里点踩,我添加了这个答案,如果有人正在寻找使用reduceRight的解决方案(它类似于reduce)。谢谢。 - Nitish Narang

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接