JavaScript ES6中有一种功能性方法可以初始化一个数组吗?

35

最终我放弃了,并编写了一个 for 循环来初始化一个简单的对象数组,其中每个对象都有一个递增的计数器 (id) 作为对象的属性。换句话说,我只想要:

var sampleData = [{id: 1},{id: 2},...];

我希望有一种简洁的语法可以直接放在我的返回语句中。

let sampleData = [];
for (var p = 0; p < 25; p++){
    sampleData.push({id: p});
}

return {
    data: sampleData,
    isLoading: true
};

如何在ES2015中生成从0到n的数字范围?这与以下问题密切相关,如果不是重复的:How to generate range of numbers from 0 to n in ES2015?Is there a mechanism to loop x times in ES6 without mutable variables?functional way to iterate over range in ES6 - Bergi
6个回答

58
Array.from()是一个不错的方法。你可以传递一个{length: somlength}对象或其他类数组对象,以及定义每个项的函数。该函数的第一个参数(将其称为_只是表示它未被使用)将是我们传入的数组中的项(但我们只传入了长度,因此没有意义),第二个i是索引,用于你的id

let sampleData = Array.from({length: 10}, (_, id) => ({id}))

console.log(sampleData)


不错的解决方案@Mark,我以为“underline”是lodash的东西。您有哪些指针可以让我了解更多关于“underline”的信息? - Pete
5
@Pete,你所询问的东西看起来像是一个字符没有传递过来。我加了一点关于“_”的说明,它只是一个函数参数--可以随便取任何名字。我有时会用“_”表示将被未使用或未定义的参数。 - Mark
1
是的,我是指下划线。非常优雅。当然,我需要将其展开以便我能理解它。我会在 Stack Overflow 允许我的时候立即检查您的答案。 - Pete
1
@Pete:添加_是因为Mark只需要第二个参数,所以通常使用_跳过不需要的参数。当然,除非你正在使用lodash - Isaac
2
此外,很多编程语言将“_”作为实际的语言特性。它会忽略该值。我能想到的两个例子是Rust和Haskell。在Javascript中,“_”纯粹是一种约定,并且实际上被赋予了一个值。“const a = _ =>“ hi” + _;”是有效的代码。它只是一个标识符。它是为了传达意图而不是改变它的工作方式。 - Nathaniel Pisarski

19

我通常做的是这样:

const data = Array(10).fill().map((v, i) => ({id: i + 1}));
console.log({data});

fill 确保它可以与 map 一起使用。


这似乎与JohnP的解决方案等效,但是这个fill()比展开运算符(fill()VS...JSBench.me测试)快10%以上,但是与Mark的Array.from(...)解决方案相比,它们都慢了30%以上。 - CPHPython

9
您可以使用spread操作符与Array一起使用,然后将每个undefined元素映射到您想要的对象。

var arr = [...Array(10)].map((_,i)=>({id:i}));
console.log(arr)


为什么需要使用展开运算符?是因为否则你将在一个空的稀疏数组上进行映射,该数组没有任何项吗? - wizzwizz4
4
是的,Array(10) 只是设置了数组的 length 属性,但其中没有任何元素。 - JohanP
2
假设 [...Array(10)] 相当于 Array(10).fill(),那么后者更易读吗?诚然,我对人们在 JavaScript 中发现什么清晰的直觉并不是很好。 - JollyJoker

6
您正在寻找一种变形,即反向折叠 -

// unfold : ((r, state) -> List r, unit -> List r, state) -> List r
const unfold = (f, init) =>
  f ( (x, next) => [ x, ...unfold (f, next) ]
    , () => [] 
    , init
    )
    
// sampleData : List { id: Int }
const sampleData =
  unfold
    ( (next, done, i) =>
        i > 25
          ? done ()
          : next ({ id: i }, i + 1)
    , 0
    )
    
console .log (sampleData)
// [ { id: 0 }, { id : 1 }, ... { id: 25 } ]

通过在其他常见程序中看到unfold的使用方法,您可以直观地了解它的工作原理 -

// unfold : ((r, state) -> List r, unit -> List r, state) -> List r
const unfold = (f, init) =>
  f ( (x, next) => [ x, ...unfold (f, next) ]
    , () => []
    , init
    )
    
// fibseq : Int -> List Int
const fibseq = init =>
  unfold
    ( (next, done, [ n, a, b ]) =>
         n === 0
           ? done ()
           : next (a, [ n - 1, b, a + b ])
    , [ init, 0, 1 ]
    )
    
console .log (fibseq (10))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]

unfold 的实现只是一种可能性。动手试试,按照自己的想法实现它。

// type Maybe a = Nothing | Just a    

// Just : a -> Maybe a
const Just = x =>
  ({ match: ({ Just: f }) => f (x) })

// Nothing : unit -> Maybe a
const Nothing = () =>
  ({ match: ({ Nothing: f }) => f () })

// unfold : (state -> Maybe (a, state), state) -> List a  
const unfold = (f, init) =>
  f (init) .match
    ( { Nothing: () => []
      , Just: ([ x, next ]) => [ x, ...unfold (f, next) ]
      }
    )

// fibseq : Int -> List Int
const fibseq = init =>
  unfold
    ( ([ n, a, b ]) =>
        n === 0
          ? Nothing ()
          : Just ([ a, [ n - 1, b, a + b ] ]) // <-- yikes, read more below
    , [ init, 0, 1 ]
    )
    
console .log (fibseq (10))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]

我在上面作弊了,使用[]作为元组。这使程序更短,但最好明确地建模并考虑其类型。你用functional-programming标记了这个问题,因此值得额外努力来从我们的程序中去除这种隐式处理。通过将其显示为单独的步骤,我们隔离了一种技术,不仅可以应用于unfold,还可以应用于我们设计的任何程序 -

// type Maybe a = Nothing | Just a
// type Tuple a b = { first: a, second: b }

// Just : a -> Maybe a
const Just = x =>
  ({ match: ({ Just: f }) => f (x) })

// Nothing : unit -> Maybe a
const Nothing = () =>
  ({ match: ({ Nothing: f }) => f () })

// Tuple : (a, b) -> Tuple a b
const Tuple = (first, second) =>
  ({ first, second })

// unfold : (state -> Maybe Tuple (a, state), state) -> List a  
const unfold = (f, init) =>
  f (init) .match
    ( { Nothing: () => []
      , Just: (t) => [ t.first, ...unfold (f, t.second) ] // <-- Tuple
      }
    )

// fibseq : Int -> List Int
const fibseq = init =>
  unfold
    ( ([ n, a, b ]) =>
        n === 0
          ? Nothing ()
          : Just (Tuple (a, [ n - 1, b, a + b ])) // <-- Tuple
    , [ init, 0, 1 ]
    )
    
console .log (fibseq (10))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]


非常好的反形态实现。不幸的是,“ana”有时候并不足够:“tails(“ana”)”产生了[“ana”,“na”,“a”,“”]。这不能用“ana”来实现,因为“Maybe”没有为基本情况提供值。如果将“Mabye”与“Either”结合起来,您将获得一个更通用的形态学,称为“apo”。我把“apo”想象成“ana”的大而善良的兄弟。愉快的(展开/折叠)学习:D - user5536315
非常感谢,鲍勃。确实,使用ana实现tails有些棘手,但是使用复合状态仍然是可能的。感谢分享apo。我喜欢接触范畴论,但很少有时间自己深入探索这个密集的主题。其他读者如果感到迷失,可以从阅读关于apomorphism的文章开始。 - Mulan
顺便说一下,你应该教我如何使用apo编写tails :D - Mulan
这个无法实现 - 通常我不会做出如此绝对的断言。如果我学到了新东西,我会感到非常兴奋。无论如何,这是我使用apo的第一个粗略实现tails。你可能不喜欢它,因为它相当命令式(跳板,本地变异)等等,所以我最终可以在生产中使用它。 - user5536315
递归方案不需要“Fix”也是值得学习的。zygo编码了两个折叠,其中后一个依赖于前一个。mutu抽象出相互递归,同时结合了两个折叠。histo使您的代数可以访问所有中间结果。至于futu,嗯,我还没有完全掌握... - user5536315
显示剩余2条评论

4
< p > < code > .from() 的例子很好,但如果您想要更有创意的话,请看这个。

const newArray = length => [...`${Math.pow(10, length) - 1}`]
newArray(2)
newArray(10)

大规模限制但是。
newArray(1000)
["I", "n", "f", "i", "n", "i", "t", "y"]

7
这并未真正回答问题,但出于趣味和创造力的精神,给你加一个赞(+1)。 - Jared Smith

3
你可以使用一个简单的递归过程来实现这一点。
const iter = (arr, counter) => {
  if (counter === 25) return arr;
  return iter([...arr, {id:counter}], counter + 1)
}
iter([], 0)

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