びぼーろくっ!

誰かに見せるわけでもないけど、備忘録として。。

トランザクションで迷い頭の中で議論が起きた話。

こういう処理をしたいとします。
・大量のデータ&APIのリクエストを伴うため、1件ずつトランザクションを切らないと駄目。
そこで大量のデータのループ内でトランザクションを作成して、commit or rollbackすると思うのですが問題は例外発生後の処理ですね。

例:x~z~yのループ内で1件ずつTransactionを切るとして、z件目に例外が発生した場合
・z件目はrollbackして例外を握り潰してしてz~y件目を実行。
・z件目で例外発生して以後の処理は実行しない
↑2つのどちらかだと思うのだけど・・・結構ケースバイケースな事象な気がしてならないんです。

        //ケース1
        public async Task LoopTransactionCase1()
        {
            var idList = await repository.GetIdList().ConfigureAwait(false);
            foreach (var id in idList)
            {
                using (var scope = new TransactionScope(TransactionScopeOption.Required, 
                    new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted },
                    TransactionScopeAsyncFlowOption.Enabled))
                {
                    //POSTデータを取得して作成
                    var data = await repository.GetPostData(id).ConfigureAwait(false);
                    var content = new StringContent(JsonConvert.SerializeObject(data));
                    content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

                    //POST経由でAPIにリクエストを飛ばす。戻りはboolean
                    //httpClientが失敗した場合、例外を潰しfalseを返却する。
                    var response = await httpClient.PostAsync("url", content).ConfigureAwait(false);
                    if (!await deserializeResponse<bool>(response))
                    {
                        throw new SystemException("failed to data registration.");
                    }
                    scope.Complete();
                }
            }
        }

        //ケース2
        public async Task LoopTransactionCase2()
        {
            var idList = await repository.GetIdList().ConfigureAwait(false);
            foreach (var id in idList)
            {
                using (var scope = new TransactionScope(TransactionScopeOption.Required,
                    new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted },
                    TransactionScopeAsyncFlowOption.Enabled))
                {
                    var data = await repository.GetPostData(id).ConfigureAwait(false);
                    var content = new StringContent(JsonConvert.SerializeObject(data));
                    content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

                    var response = await httpClient.PostAsync("url", content).ConfigureAwait(false);
                    if (!await deserializeResponse<bool>(response))
                    {
                        
                        scope.Dispose();
                        continue;
                    }
                    scope.Complete();
                }
            }
        }

問題はケース1の場合、不正データがz件目に存在していた場合以後のデータが処理されないことです。
そのため不正なデータのせいでz + 1 ~ y件までのデータ処理が正常であっても走らない事が問題です。

ケース2の問題としては仮にx~z~yが10000件を超えるデータは全て正常だけどリクエスト先の鯖が落ちてる or clientの通信が切れてる。
などの問題があった場合、無駄な処理を10000件を実行することになります。

うーん、これはどっちがいいんだろう。。。

モデル同士の値を比較する共通メソッドをつくった。

C#やっぱしんどいです。
今オブジェクトの比較事態はJSON文字列に変換して行っているのですが、Nunitの仕様上擦り切れてしまうんですよね。。
コンソール画面で色つきで表示されたら不正な値が見れるからいいのになー。と思ってて探してみたけど無かったので作りました。

結構無理やりな勢いで作ったので普通にバグるかもしれません。。使用するのは自己責任で・・・
JavaScriptなら簡単にいけちゃうのにな。。。やっぱC#しんどry
実際にモデルの比較ならJSONに変換したほうがイケてると思います。今回は細かい検証用なので使い道が少ないかも。。

    public void AssertResultConsoleView<T>(T modelA, T modelB)
    {
        var propsA = modelA.GetType().GetProperties();
        var propsB = modelB.GetType().GetProperties();

        var i = 0;
        foreach (var prop in propsA)
        {
            Console.ResetColor();
            var propB = propsB[i];
            if (prop.Name == propB.Name)
            {
                var aValue = prop.GetValue(modelA).ToString();
                var bValue = propB.GetValue(modelB).ToString();

                if (aValue != bValue)
                {
                    Console.BackgroundColor = ConsoleColor.Red;
                }
                Console.WriteLine("比較結果:{0}: exp:{1} result:{2}", prop.Name, aValue, bValue);
            }
            i++;
        }
    }

知らなかった!SELECT文の結果をINSERTする方法

今回はSQLです。(postgresqlを使用)
本番環境のマスタースクリプトを更新する作業があったのですが、本番環境のデータは参照できないので
正規表現などで一気にスクリプトファイルを組むわけにもいかずもやもやしてました。

調べた結果SELECT-INSERTなるものがあったのですね!
(DB周りは他の人が担当してたので詳しい知識はなかった orz)

こんなテーブルやレコードがあるとします。

insert_table

id colomn2 colomn3
null null null

select_table

id birthday job
1 1995/01/01 developer
2 1996/01/01 lawyer
3 1997/01/01 docter

やりたい事はinsert_tableにselect_tableのidを反映したい場合です。
固定値が混ざったレコードを登録したい場合はSELECT文に固定値を書き込めばいいです。

    • insert_table(登録したいレコードのテーブル
    • select_table(既に存在するテーブル)
INSERT INTO insert_table(
     id, column2, column3
)
SELECT id, '固定値', now()
FROM   select_table;

結果をみてみましょう・・・

SELECT * FROM insert_table;

insert_table

id colomn2 colomn3
1 固定値 2017/4/6
2 固定値 2017/4/6
3 固定値 2017/4/6

今日は以上です。

どうでもいいけど・・・はてぶろってpostgresqlの記法ってないのかな・・
mysqlでコードハイライトしたけど、それっぽいのが見つからなかった・・・

MockHttpでHttpResponseをモック化!

最近C#ばっかりです。
なかなか型の制約とかHttpContextがHttpContextBaseを継承してなかったりとか色々としんどいです。

今回戸惑ったのがAPIのRequest⇔ResponseをMock化したかったのですが
NUGET辺りで探してたらいいのがありました。

これです。
github.com


今回はNUnitFrameWorkを使用しています。

using Newtonsoft.Json;
using NUnit.Framework;
using RichardSzalay.MockHttp;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Exsample.Test.Common.Util;
using Exsample.Net.Http;
using Moq;
using System.Web;
using System;

namespace Exsample.Module.Test
{
    [TestFixture]
    public class GetStreamAsync : ClientContext
    {
        private string url;
        private Stream stream;
        private Error error;

        /// <summary>
        /// 成功時(status=OK)のMockResponseMessageをセット
        /// </summary>
        private void setSuccessContent()
        {
            mockHttpMessage.Expect($"{baseUrl}*")
                .Respond(() =>
                {
                    // Status 200時に実行されるCallBack。
                    // Task<HttpResponseMessage>型を返すようにする。
                    return Task.Factory.StartNew<HttpResponseMessage>(() =>
                    {
                        var res = new HttpResponseMessage();
                        res.Content = new StreamContent(stream);
                        return res;
                    });
                });
        }

        /// <summary>
        /// 失敗時(status=NG)のMockResponseMessageをセット
        /// </summary>
        private void setFailContent()
        {
            error = new Error();
            // シリアライズして文字列として第二引数に渡してます。
            var content = JsonConvert.SerializeObject(error);
            mockHttpMessage.Expect($"{baseUrl}*")
                .Respond(HttpStatusCode.InternalServerError, "application/json", content);
        }

        /// <summary>
        /// MOCKのセットアップ。
        /// </summary>
        /// <param name="isSuccess">成功テストか否か</param>
        public void mockSetup(bool isSuccess)
        {
            if (isSuccess)
            {
                // テスト用のダミーStreamを作成
                stream = StreamCreator.Create();
                url = "unittest/getasync";
                setSuccessContent();
            }
            else
            {
                url = "unittest/notfound";
                setFailContent();
            }
        }

        /// <summary>
        /// Status=OKのテスト
        /// </summary>
        /// <returns></returns>
        [Test]
        public async Task Success()
        {
            mockSetup(true);
            var result = await client.GetStreamAsync(url, null);
            Assert.AreEqual(stream, result);
        }

        /// <summary>
        /// Status=NGのテスト
        /// </summary>
        /// <returns></returns>
        [Test]
        public void Fail()
        {
            mockSetup(false);
            var exception = Assert.ThrowsAsync<ResponsException>(() =>
            {
                return client.GetStreamAsync(url, null);
            });
            Assert.AreEqual(HttpStatusCode.InternalServerError, exception.StatusCode);
            // クラス同時の比較は出来ないのでJSON化して比較
            AssertObject(error, JsonConvert.DeserializeObject<Error>(exception.Message));
        }
    }

    /// <summary>
    /// パラメーターのインスタンス化や共通のMock化を行う
    /// </summary>
    public class ClientContext : TestBase
    {
        protected TargetClient client; // テスト対象のメソッド
        Mock<HttpContextBase> mockContext;
        protected MockHttpMessageHandler mockHttpMessage;
        protected Guid userId;

        // 適当なURL
        protected string baseUrl = "http://exsample.com:8080/api/";

        protected class Error
        {
            public string ErrorDiscription { get; set; }
            public string ErrorMessage { get; set; }
            public Error()
            {
                this.ErrorDiscription = "error_description";
                this.ErrorMessage = "server_error";
            }
        }

        [SetUp]
        public void Setup()
        {
            // MockのHttpContextBaseを作成
            mockContext = FakeHttpContextBase.Create(); // HttpContextBaseをMock化をする。

            // Sessionを上書きする
            var session = new Mock<HttpSessionStateBase>();
            userId = Guid.NewGuid();
            session.Setup(x => x["userId"]).Returns(userId.ToString());
            mockContext.Setup(x => x.Session).Returns(session.Object);

            // 今回紹介するmockHttpMessageHandlerのインスタンスを作成。
            mockHttpMessage = new MockHttpMessageHandler();
            var httpClient = new HttpClient(mockHttpMessage);

            client = new TargetClient(mockContext.Object);
            client.HttpClient = httpClient;
        }
    }
}

テスト対象メソッドはStatus=NGならばResponsExceptionをスロー。
メッセージ部分はJSON形式にて返却されます。
成功時は返却されたHttpResponseMessage.ContentをStreamにして返却しています。

APIのクライアント部分だし通信されると困るので、何かいいメソッドがないか見てたらいいのを見つけました。
今度もこれを使ってゴリゴリ書いていきたいと思います。

gulpを使ってlessをコンパイルする方法

今Electronを使ってデスクトップアプリを趣味で開発しているのですが
猛烈にlessを導入したくなったので色々と設定してみました。

less
lesscss.org


ただ、Client変換方式でlessを導入するのはブラウザやパフォーマンスの問題があるので
lessファイルをコンパイルしてくれてコンパイル後のcssを読み取るように設定してみます。
・・もちろんlessの公式にあるとおり

 lessc styles.less styles.css

このようなコマンドでも実行は可能ですが、いちいちCSSの編集後にこのコマンドを投げるのは面倒です。
そのため保存時に自動的にコンパイルが走り、特定のフォルダ内にCSSが作成されるようにします。

上記のことをやりたい場合、gulp + gulp-lessを使用すると便利です。

gulp.js

www.npmjs.com

まずはプロジェクトにlessを導入

npm install --save-dev less

そしてgulp-less(gulp上でlessをビルドしてくれる機能)も導入します。

npm install --save-dev gulp-less

そして新規にgulpfile.jsをルートに作成します。

//gulpfile.js
const gulp = require('gulp');
const less = require('gulp-less');

// less → cssにコンパイルするタスク
gulp.task('less', function(){
  src('./src/less/*.less')
  .pipe(less())
  .pipe(gulp.dest('/src/css/*'));
});

//ファイルの変更があった場合、実行されるタスク
gulp.watch('watch', ['less'], function() {
  gulp.watch('./src/less/*.less', ['less']);
});

// デフォルトで実行してもらいたいタスクを配列で定義する。
gulp.task('default', ['less']);

すごく直感的に書けると思います。
まずはgulp.taskの第一引数に'less'という名前を指定しました。
これでgulp lessというコマンドを実行したら第二引数のcallbackが実行されます。
callbackの中身はlessファイルをcssコンパイルするという動作ですね。

そしてgulp.watchでファイルの変更があった場合(このソースの場合、.src.less/*.lessの変更)
gulp.taskで指定されたlessを実行するという定義になります。

また、第二引数の['less']ですがgulp watchを実行する前に一度走らせたいタスクを配列で記入していきます。
gulp watchを実行する前にlessを変更した場合にも実行して貰いたいので。

最後にpackage.jsonにこちらを記入します。
今回はlessのコマンド実行なのでスクリプト名をlessにしていますが任意の名前で大丈夫です。

//package.json
scripts: {
    "watch":  "gulp watch";
}

そしてpackage.jsonのある階層で下記を実行するとコンパイルされるはずです。

npm run watch

設定自体楽チンで、何よりcssよりも綺麗に書けるのでlessの導入はお勧めです。ぜひgulpをお試し下さい!

仕様変更で没になったので・・・

仕様変更で没になったので・・・

せっかく書いたのに悔しいので載せます。
ソースを見れば一目ですが、HTTPでリクエストを飛ばす処理になってます。
時間が無くて動作確認してないです。多分POSTでつまづくかも。。


簡単な解説
DELETEやPUT、PATCHは未記入ですが同じような要領でいけます。
戻りをTモデルにしてあるのでどのクラスでもマッピングしてくれるように書きました。
リクエスト先の戻りはJSONを想定して書いてます。

using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
using System.Collections;

namespace MyNameSpace.Request
{
    public class RequestService
    {
        /// <summary>
        /// GETメソッド
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<T> Get<T>(string url, Dictionary<string, dynamic> parameters = null)
        {
            var queryParams = createQueryParameter(parameters);
            var http = new HttpClient();
            http.DefaultRequestHeaders.Add("contentType", "application/json");

            var res = await http.GetAsync(url + queryParams);
            var body = await res.Content.ReadAsStringAsync();

            return serializeBody<T>(body);
        }

        /// <summary>
        /// POST
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<T> Post<T>(string url, object data)
        {
            var http = new HttpClient();
            http.DefaultRequestHeaders.Add("contentType", "application/json");

            var param = createNameValueCollection(data);
            var content = new FormUrlEncodedContent(param.ToArray());
            var body = "";
            try
            {
                var res = await http.PostAsync(url, content);
                body = await res.Content.ReadAsStringAsync();
            }
            catch (Exception e)
            {
                var msg = e.Message;
            }
            return serializeBody<T>(body);
        }

        /// <summary>
        /// 受け取ったJSONをTモデルにシリアライズする
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private T serializeBody<T>(string body)
        {
            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(body)))
            {
                var serializer = new DataContractJsonSerializer(typeof(T));
                return (T)serializer.ReadObject(stream);
            }
        }

        /// <summary>
        /// 送信用のデータをキャストする
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private List<KeyValuePair<string, string>> createNameValueCollection(object data)
        {
            var bodies = new List<KeyValuePair<string, string>>();
            var type = data.GetType();
            foreach (var prop in type.GetProperties())
            {
                var name = prop.Name;

                if (prop.PropertyType.IsArray)
                {
                    var values = (IEnumerable)prop.GetValue(data, null);
                    foreach (var arrProp in values)
                    {
                        var arrValue = arrProp.ToString();
                        bodies.Add(new KeyValuePair<string, string>(name, arrValue));
                    }
                } else if (prop.PropertyType.Name == "DateTime") {
                    var value = ((DateTime)prop.GetValue(data, null)).ToShortDateString();
                    bodies.Add(new KeyValuePair<string, string>(name, value));

                } else {
                    var value = prop.GetValue(data, null).ToString();
                    bodies.Add(new KeyValuePair<string, string>(name, value));
                }
            }
            return bodies;
        }

        /// <summary>
        /// GET専用・クエリパラメータを作成
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private string createQueryParameter(Dictionary<string, dynamic> parameters)
        {
            if (parameters == null || parameters.Count == 0) return "";
            var list = new List<string>();
            foreach(var param in parameters)
            {
                var builder = new StringBuilder();
                builder.Append(param.Key).Append("=").Append(param.Value);
                list.Add(builder.ToString());
            }
            var arr = list.ToArray();
            return "?" + string.Join("&", arr);
        }
    }
}

javascriptの配列の関数を並べてみました。

殆どMDNからのパクリです。
なんとなくforEachで片付けてしまうところとかもあるし、必要に応じて使い分ければ冗長なコードを書かなくて済むし
なんとなく使いこなせたらかっこいい気がするw
ので、並べてみました。分からなかったらMDNや他のサイトに飛んでください。

足りないものがあれば追記していく予定です。


concat
配列と配列を結合する。
戻り:配列インスタンス
配列以外の値を引数に指定した場合、末端にappendされる。
引数に何も指定してなければ、新しい配列がコピーされる。

    const arr = ['a', 'b', 'c', 'ab', 'bc'];
    const concat = arr.concat(['d', 'e']);
    // concat: ['a', 'b', 'c', 'ab', 'bc', 'd', 'e']
    const concat2 = arr.concat('d');
    // concat2: ['a', 'b', 'c', 'ab', 'bc', 'd']
    const concat3 = arr.concat();
    // concat3: ['a', 'b', 'c', 'ab', 'bc']

every
引数に渡されたcallback関数を実行。
callback内でfalseがl返されたらその時点でループは終了。戻りはfalseとなる。
ループ終了後、falseがなければ戻りはtrueとなる。

    const arr = ['a', 'b', 'c', 'ab', 'bc'];
    const every = arr.every((val) => {
      return typeof val === 'string';
    });
    //  every: true;
    const every2 = ['a', 'b', 'c', 'ab', 1, 'bc'].every((val) => {
      return typeof val === 'string';
    });
    //  every2: false;

filter
引数に渡されたcallback関数を実行。
戻り:条件に一致する配列の要素を返却し、新しい配列を作成する。

    const arr = ['a', 'b', 'c', 'ab', 'bc'];
    const filter = arr.filter((val) => {
      return val.indexOf('a') !== -1;
    });
    //  filter: ['a', 'ab'];

forEach
callBackの関数を順番に実行する。
戻りはvoid;

    const arr = ['a', 'b', 'c', 'ab', 'bc'];
    let forEach = '';
    arr.forEach((val, i) => {
      forEach += val;
      forEach += (i !== arr.length - 1) ?
        'と' : '';
    });
    //  forEach: aとbとcとabとbc

indexOf
配列の要素の中から完全一致するものを探す。
戻りは要素の最初の添字(位置)存在しない場合は-1

    const arr = ['a', 'b', 'c', 'ab', 'bc'];
    const indexOf = arr.indexOf('ab');
    //  indexOf : 3

join
配列と配列を結合して文字列に返却。
対象の配列が空の場合は空文字を返却

    const arr = ['a', 'b', 'c', 'ab', 'bc'];
    const join = [].join();
    //  join : '' //空文字

    const join2 = [].join(',');
    //  join2 : '' //空文字

    const join3 = arr.join();
    //  join3: a,b,c,ab,bc

    const join4 = arr.join('と');
    //  join4: aとbとcとabとbc

lastIndexOf
配列の要素の中から完全一致するものを探す。
戻りは要素の添字(位置)存在しない場合は-1
indexOfとの違いは見つかった最後の添字という点。

    const arr = ['a', 'b', 'b', 'c'];
    const lastIndexOf = arr.lastIndexOf('b');
    //  lastIndexOf : 2

    //  ちなみにindexOfでは
    const indexOf = arr.indexOf('b');
    //  indexOf : 1;

map
全ての配列要素に対し処理をし、その結果新しい配列の結果を返却する。
forEachだと冗長な処理になりがちだが、これを使用するとスッキリかけることがある。

    const numArr = [2, 3, 4, 5];
    const map = numArr.map((val) => {
      return val * 2;
    });
    //  map : [4, 6, 8, 10];

pop
配列から最後の要素を取り出し、取り出した要素を戻りとして返却する。
もし配列が空だった場合、戻りはundefinedになる。

    const arr = ['japan', 'america', 'rusia'];
    const pop = arr.pop();
    //  arr: ['japan', 'america'] // 末端の要素が取り出されて配列数が1減る。
    //  pop: 'rusia'

push
配列に要素を追加する。戻りは追加後の配列数。

    const arr = ['japan', 'america', 'rusia'];
    const push = arr.push('germany');
    //  push : 4
    //  arr: ['japan', 'america', 'rusia', 'germany']

reduce
アキュムレータと配列の各要素に対して(左から右へ)関数を適用し、単一の値にする。
ちょっと理解ができないので、実際に書いてみる。

    const numArr = [2, 3, 4, 5];
    const reduce = numArr.reduce((preVal, crrVal, index) => {
      console.log(`${index}: preval:${preVal} crrVal:${crrVal}`);
      return preVal + crrVal;
    });
    //  1: preval:2 crrVal:3
    //  2: preval:5 crrVal:4
    //  3: preval:9 crrVal:5
    //  reduce: 14

まずpreviousValueとcurrentValueを使用して処理を実施して、
その結果がpreValに代入されている。
crrValは前回のcrrValの次の要素・・・。ということでいいのか?
なんとなくわかったような。そうではないような。。。
と思ったけど、こんな使い方なら良さそう。

    //  二次元配列を一次元配列にする
    const associativeArray = [['a', 'b'], ['c', 'd'], ['e', 'f']];
    const reduce2 = associativeArray.reduce((preArr, crrArr) => {
      return preArr.concat(crrArr);
    });
    //  reduce2: ['a', 'b', 'c', 'd', 'e', 'f']

reduceRight
reduceとは逆で右から左へと処理の順番が異なる。それ以外は同じ。

    const associativeArray = [['a', 'b'], ['c', 'd'], ['e', 'f']];
    const reduceRight = associativeArray.reduceRight((preArr, crrArr) => {
      return preArr.concat(crrArr);
    });
    //  reduceRight: ['e', 'f', 'c', 'd', 'a', 'b']

reverse
配列を逆さまにする。最初の配列要素が最後、最後の要素が最初。という感じ。
また、新しい配列インスタンスは作らないので、元の配列の順序も変わります。

    const arr = ['a', 'b', 'c', 'ab', 'bc'];
    const reverseArr = arr.reverse();
    //  arr: ['bc', 'ab', 'c', 'b', 'a']

shift
配列の最初の要素(0番目)を取り除き、取り除かれたあとの配列の要素の添字は1マイナスになる。
popの反対というイメージ。

    const arr = ['japan', 'america', 'rusia'];
    const shift = arr.shift();
    //  arr: ['america', 'rusia']
    //  shift: 'japan'

slice
配列の一部を取り出して新しい配列を作成。
配列のシャローコピーも可能。

    const arr = ['japan', 'america', 'rusia', 'germany', 'france'];
    //  begin・・・取り出す位置
    //  end・・・どこまで取り出すかを決めた位置

    //  slice(1, 3) は1番目の要素から4番目の要素まで
    //  (添字が 1, 2, の要素) を取り出します。
    const slice = arr.slice(1, 3);
    //  slice: ['america', 'rusia']
    //	arr: ['japan', 'america', 'rusia', 'germany', 'france'];
    //  元の配列に変更は加えないので、arrは変わることはない。

シャローコピーの実施と確認

    const arr = ['japan', 'america', 'rusia', 'germany', 'france'];
    const slice = arr.slice();
    
    //  配列の要素内の比較
    const result = JSON.stringify(arr) === JSON.stringify(slice);
    console.log(result); // true
    
    //  きちんと値渡しになっているかを確認
    slice[0] = 'finland';
    const result2 = JSON.stringify(arr) === JSON.stringify(slice);
    console.log(result2); // false

sort
配列を並び替えする。

    const arr = ['japan', 'america', 'rusia'];
    arr.sort();
    //  sortArr: ['america', 'japan', 'rusia']
    
    const numArr = [2, 10, 3, 100];
    numArr.sort();
    //  numArr: [10, 100, 2, 3]

どうやら数字も文字列としてみているような挙動なので、
数字のソートはcallbackで対応する。

    // callBackでaとbを比較してあげるだけで簡単にソートできる。
    const numArr = [2, 10, 3, 100];
    numArr.sort((a, b) => {
      return a - b;
    });
    //  numArr: [2, 3, 10, 100]
    //  a - bをb - aにすると降順になる。

some
引数に渡されたcallback関数を実行。
callback内でtrueがl返されたらその時点でループは終了。戻りはtrueとなる。
ループ終了後、trueがなければ戻りはfalseとなる。

    const arr = ['a', 'b', 'c', 'ab', 'bc'];
    const some = arr.some((val) => {
      return typeof val === 'number';
    });
    //  some: false;
    const some2 = arr.some((val) => {
      return typeof val === 'string';
    });
    //  some2: true;

splice
説明が厄介な関数。
古い要素を取り除きつつ新しい要素を追加することで、
配列の内容を変更します。
とMDNには記載があるが分かりづらいのでソースに落とし込みます。

spliceの引数は3つ
array.splice(index, howMany, [element1][, ..., elementN]);


index: 配列を変化させ始める要素の添字。arr.lengthより大きい場合、arr.lengthと同じ値
値が負数の場合、配列の終端からその値を引いた数が開始位置となる。

howMany: 配列から取り除く要素の数を示す。0の場合は取り除かれない。
引数無しの場合、index以降の全ての要素が取り除かれる。

element1, ...elementN
追加する要素。要素指定がなければ配列から要素を取り除く

戻りは取り除かれた配列。何も取り除かれなかった場合、空配列が返却される。

    // 空の配列を返却。
    const arr = ['japan', 'america', 'rusia'];
    const splice = arr.splice();
    //  splice: []
    //  arr: ['japan', 'america', 'rusia']
    // 2番目以降の配列を全て取り除く
    const arr = ['japan', 'america', 'rusia'];
    const splice = arr.splice(1);
    //  splice: ['america', 'rusia']
    //  arr: ['japan']
    //  2番目(index)の位置に0個(howMany)取り除き、germany(elem)を追加する。
    const arr = ['japan', 'america', 'rusia'];
    const splice = arr.splice(1, 0, 'germany');
    //  splice: []  // 何も取り除かれていない。
    //  arr: ['japan', 'germany', america', 'rusia']
    //  2番目(index)の位置に1個(howMany)取り除く。
    const arr = ['japan', 'america', 'rusia'];
    const splice = arr.splice(1, 1);
    //  splice: ['america']
    //  arr: ['japan', 'rusia']
    //  2番目(index)の位置に1個(howMany)取り除き、germany(elem)を追加する。
    const arr = ['japan', 'america', 'rusia'];
    const splice = arr.splice(1, 1, 'germany');
    //  splice: ['america']
    //  arr: ['japan', 'germany', 'rusia']
    //  1番目(index)の位置に2個(howMany)取り除き、germany(elem)を追加する。
    const arr = ['japan', 'america', 'rusia'];
    const splice = arr.splice(0, 2, 'germany');
    //  splice: ['japan', 'america']
    //  arr: ['germany', 'rusia']

unshift
配列の最初に要素を追加し、新しい配列数を返却します。

    const arr = ['japan', 'america', 'rusia'];
    const unshift = arr.unshift('germany');
    //  unshift: 4
    //  arr: ['germany', 'japan', 'america', 'rusia']