Skip to content

Commit

Permalink
feat(array): Array#flatを追加 (asciidwango#890)
Browse files Browse the repository at this point in the history
* feat(array): Array#flatを追加

* fix(doctest): support doctest:ecmascript directive

* fix(CONTRIBUTING): doctest:ecmascriptを追加

* fix

* fix

* fix
  • Loading branch information
azu authored Jul 24, 2019
1 parent 35d1add commit 08aed4a
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 9 deletions.
16 changes: 16 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,22 @@ DoctestでPromiseやAsync Functionを使った非同期のテストも書けま

Note: `vm`モジュールの制約からタイムアウト指定の時間が正しく指定させていることが前提となっています。


#### DoctestのECMAScriptバージョン

DoctestはNode.jsで実行されます。
実行するNode.jsがECMAScriptの最新のバージョンをサポートしていない場合があります。
そのため、コードのECMAScriptバージョンを指定することで、そのDoctestをスキップできます。

例) DoctestがECMAScript 2019であることを表記する

<!-- doctest:ecmascript: 2019 -->
```js
[1,[2], [3]].flat();
```

DoctestでサポートしてないECMAScriptバージョンのテストは実行されません。

#### Doctestの無視

CodeBlockの手前に`<!-- doctest:disable -->`というHTMLコメントがある場合はDoctestをしません。
Expand Down
28 changes: 28 additions & 0 deletions source/basic/array/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,34 @@ const newArray = array.concat("新しい要素");
console.log(newArray); // => ["A", "B", "C", "新しい要素"]
```

## [ES2019] 配列をフラット化 {#flat}

`Array#flat`メソッドを使うことで、多次元配列をフラットな配列に変換できます。
引数を指定しなかった場合は1段階のみのフラット化ですが、引数にわたす数値でフラット化する深さを指定できます。
配列をすべてフラット化する場合には、無限を意味する`Infinity`を値として渡すことで実現できます。

{{book.console}}
<!-- doctest:ecmascript: 2019 -->
```js
const array = [[["A"], "B"], "C"];
// 引数なしは 1 を指定した場合と同じ
console.log(array.flat()); // => [["A"], "B", "C"]
console.log(array.flat(1)); // => [["A"], "B", "C"]
console.log(array.flat(2)); // => ["A", "B", "C"]
// 全てをフラット化するには Inifinity を渡す
console.log(array.flat(Infinity)); // => ["A", "B", "C"]
```

また、`Array#flat`メソッドは必ず新しい配列を作成して返すメソッドです。
そのため、これ以上フラット化できない配列をフラット化しても、同じ要素をもつ新しい配列を返します。

{{book.console}}
<!-- doctest:ecmascript: 2019 -->
```js
const array = ["A", "B", "C"];
console.log(array.flat()); // => ["A", "B", "C"]
```

## 配列から要素を削除 {#delete-element}

### `Array#splice` {#splice}
Expand Down
1 change: 1 addition & 0 deletions source/basic/date/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ console.log(invalid.toString()); // => "Invalid Date"
`getMonth`メソッドや`setMonth`メソッドのように月を数値で扱うメソッドは、0から11の数値で指定することに注意しましょう。ある`Date`のインスタンスの時刻が何月かを表示するには、`getMonth`メソッドの戻り値に1を足す必要があります。

{{book.console}}
<!-- doctest:ecmascript: 2017 -->
```js
// YYYY/MM/DD形式の文字列に変換する関数
function formatDate(date) {
Expand Down
5 changes: 3 additions & 2 deletions source/basic/object/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ console.log(obj[symbolKey2]); // => "2"
それぞれ、オブジェクトのキー、値、キーと値の組み合わせを配列にして返します。

{{book.console}}
<!-- doctest:ecmascript: 2017 -->
```js
const obj = {
"one": 1,
Expand All @@ -563,9 +564,9 @@ const obj = {
};
// `Object.keys`はキーの列挙した配列を返す
console.log(Object.keys(obj)); // => ["one", "two", "three"]
// `Object.values`(ES2017)は値を列挙した配列を返す
// `Object.values`は値を列挙した配列を返す
console.log(Object.values(obj)); // => [1, 2, 3]
// `Object.entries`(ES2017)は[キー, 値]の配列を返す
// `Object.entries`は[キー, 値]の配列を返す
console.log(Object.entries(obj)); // => [["one", 1], ["two", 2], ["three", 3]]
```

Expand Down
17 changes: 17 additions & 0 deletions test/lib/DocTestController.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
const DISABLE_PATTERN = /doctest:\s*?disable/;
const ASYNC_TIME_PATTERN = /doctest:\w*?async:(\d+)/;
const ERROR_TYPE_PATTERN = /doctest:\s*([\w\s]*?Error)/;
const ES_VERSION = /doctest:ecmascript:\s*?([\d]+)/;

/**
* CodeBlockの手前に該当するHTMLコメントはdoctestの制御コードとして扱える
Expand Down Expand Up @@ -83,6 +84,22 @@ class DocTestController {
return timeoutMillSecAsString;
}


get ecmascriptVersion() {
const version = this.comments.find(comment => {
return ES_VERSION.test(comment);
});
if (!version) {
return;
}
const match = version.match(ES_VERSION);
const versionString = match && match[1];
if (versionString === undefined) {
throw new Error(`AsyncDocTest: ecmascript formatec: ${version}`);
}
return versionString;
}

/**
* Return true, if the `error` is expected error name
* If not defined expected error, return true.
Expand Down
14 changes: 7 additions & 7 deletions test/markdown-doc-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const getComments = (parentNode, codeNode) => {
* .travis.ymlのサポートしているNode.jsバージョンに合わせる
* @type {string[]}
*/
const ESVersions = ["ES2017"];
const ESVersions = ["2017", "2018", "2019"];
/**
* Markdownファイルの CodeBlock に対してdoctestを行う
* CodeBlockは必ず実行できるとは限らないので、
Expand All @@ -42,15 +42,15 @@ const ESVersions = ["ES2017"];
*
* その他詳細は CONTRIBUTING.md を読む
**/
describe("doctest:md", function() {
describe("doctest:md", function () {
const files = globby.sync([
`${sourceDir}/**/*.md`,
`!${sourceDir}/**/node_modules{,/**}`,
`!**/OUTLINE.md`
]);
files.forEach(filePath => {
const normalizeFilePath = filePath.replace(sourceDir, "");
describe(`${normalizeFilePath}`, function() {
describe(`${normalizeFilePath}`, function () {
const content = fs.readFileSync(filePath, "utf-8");
const markdownAST = attachParents(remark.parse(content));
const codeBlocks = [].concat(
Expand All @@ -60,16 +60,16 @@ describe("doctest:md", function() {
// try to eval
codeBlocks.forEach((codeBlock, index) => {
const codeValue = codeBlock.value;
const isIgnoredCode = ESVersions.some(version => {
return codeValue.includes(version);
});
const comments = getComments(codeBlock.parent, codeBlock);
const docTestController = new DocTestController(comments);
const isIgnoredCode = ESVersions.some(version => {
return codeValue.includes(version) || docTestController.ecmascriptVersion === version;
});
if (docTestController.isDisabled) {
return;
}
const testCaseName = codeValue.slice(0, 32).replace(/[\r\n]/g, "_");
it(testCaseName, function(_done) {
it(testCaseName, function (_done) {
let isCalled = false;
const done = (error) => {
if (isCalled) {
Expand Down

0 comments on commit 08aed4a

Please sign in to comment.