點燈坊

失くすものさえない今が強くなるチャンスよ

如何將 Object Property 的 Array 攤平 ?

Sam Xiao's Avatar 2019-08-21

實務上 API 所傳回的資料常常掛在 data Property 上,且資料藏在 Nested Property 下,但 GUI 顯示常只需要一層 Array 而已,這常見需求該如何實現呢 ?

Version

macOS Mojave 10.14.6
VS Code 1.37.1
Quokka 1.0.240
ECMAScript 2019
Ramda 0.26.1

Imperative

let data = {
  authors: {
    JavaScript: [
      'Axel Rauschmayer',
      'John Resig'
    ],
    FP: [
      'Luis Atencio'
    ]
  },
};

let fn = obj => {
  let result = [];

  for(let x in obj.authors)
    for(let y of obj.authors[x])
      result.push(y);

  return result;

};

fn(data); // ?

data 為 object,資料都在 authors property 下,但目前 author 散落在 JavaScriptFP property 下,我們希望結果為一層 array 有所有 author 方便顯示。

Imperative 做法會先宣告回傳的 result array:

  • 使用 for in loop 將 object 內所有 key 取出
  • 再透過 [] 取出 array
  • 最後使用 for of 將 array 內所有 element 取出

我們發現 imperative 要使用兩層 for loop。

flatten000

Functional

let data = {
  authors: {
    JavaScript: [
      'Axel Rauschmayer',
      'John Resig'
    ],
    FP: [
      'Luis Atencio'
    ]
  }
};

let fn = obj => Object.values(obj.authors).flat();

fn(data); // ?

Functional 會使用 ES2017 的 Object.values() 將 object 所有 value 以 array 呈現。

Object.values(obj.authors) 會回傳兩層 array,並非我們要的一層 array,再使用 ES2019 的 Array.prototype.flat() 將 array 攤平。

flatten001

Ramda

import { values, prop, pipe, flatten } from 'ramda';

let data = {
  authors: {
    JavaScript: [
      'Axel Rauschmayer',
      'John Resig'
    ],
    FP: [
      'Luis Atencio'
    ]
  }
};

let fn = pipe(
  prop('authors'),
  values,
  flatten
);

fn(data); // ?

既然有 Functional 概念,就很容易用 Ramda 實現了:

  • 先使用 prop() 取得 data.authors 的 value
  • 再使用 values() 取得 object 的所有 value,結果為 array
  • 最後使用 flatten() 將兩層 array 攤平

最後以 pipe() 整合所有流程,非常清楚,並且為 point-free。

flatten002

Conclusion

  • Object.values()Array.prototype.flat() 為 ECMAScript 較新的 function,但非常實用
  • Ramda 寫法其實與 ECMAScript 原生寫法觀念相同,只是 Ramda 的 curried function 讓程式碼更精簡

Reference

MDN, Object.values()
MDN, Array.prototype.flat()
Ramda, values()
Ramda, pipe()
Ramda, prop()
Ramda, flatten()