下記のような配列を任意のdateを起点として並べ替えたいです。
js
1var hoge = [ 2 { 3 date:"7", 4 status:"C" 5 }, 6 { 7 date:"6", 8 status:"B" 9 }, 10 { 11 date:"5", 12 status:"D" 13 }, 14 { 15 date:"4", 16 status:"B" 17 }, 18 { 19 date:"3", 20 status:"A" 21 }, 22 { 23 date:"2", 24 status:"C" 25 }, 26 { 27 date:"1", 28 status:"A" 29 } 30];
たとえば任意の値を4としたときに下記のようにしたいです。
js
1var hoge = [ 2 { 3 date:"4", 4 status:"B" 5 }, 6 { 7 date:"3", 8 status:"A" 9 }, 10 { 11 date:"2", 12 status:"C" 13 }, 14 { 15 date:"1", 16 status:"A" 17 }, 18 { 19 date:"7", 20 status:"C" 21 }, 22 { 23 date:"6", 24 status:"B" 25 }, 26 { 27 date:"5", 28 status:"D" 29 } 30];
dateが4以下の要素を並べた後に、dateが4以上の要素をdateが大きい順に並べて配列にしたいです。
初心者なので、、どなたか教えてください。。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答6件
0
dateが4以下の要素を並べた後に、dateが4以上の要素をdateが大きい順に並べて配列にしたいです。
与えられた条件でそのまま実装するなら、
JavaScript
1function sample (array, baseNumber) { 2 array.sort((a, b) => { 3 a = a.date; 4 b = b.date; 5 return (baseNumber < a) - (baseNumber < b) ? a - b : b - a; 6 }); 7} 8 9var array = [{"date":"7","status":"C"},{"date":"6","status":"B"},{"date":"5","status":"D"},{"date":"4","status":"B"},{"date":"3","status":"A"},{"date":"2","status":"C"},{"date":"1","status":"A"}]; 10 11sample(array, 4); 12console.log(JSON.stringify(array)); // [{"date":"4","status":"B"},{"date":"3","status":"A"},{"date":"2","status":"C"},{"date":"1","status":"A"},{"date":"7","status":"C"},{"date":"6","status":"B"},{"date":"5","status":"D"}]
開示されていない条件が他にあるなら、最適化の余地が残っているかもしれません。
- 配列は date プロパティ値で降順ソートされている
- 配列の date プロパティ値は等差数列の要件を満たしている
- 配列の date プロパティ値は1以上の整数である
Re: eeengineeeeeeer さん
投稿2018/09/15 00:11
総合スコア18164
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
javascriptにもともとあるarray.sortを使い、渡す比較関数を自分で書けばOKでしょう。
例えば名前をcompare(a,b)として
aとbが同列なら0
aのほうがbより先に来るように並べ替えたいなら -1
aのほうがbより後に来るように並べ替えたいなら +1
を返すように比較関数を書きます。
投稿2018/09/16 01:25
総合スコア702
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/09/16 12:45
0
ベストアンサー
こんにちは。
このご質問でeeengineeeeeeerさんが要望されている並べ替えを実現するために、異なる2つのアプローチがあると思いましたので、それぞれ以下の(1)、(2)として回答します。
なお、以下の回答では、並べ替えた結果、先頭にくるべき要素のdate
プロパティに相当する整数値を、 baseDate
という変数名で表します。ご質問の例:
たとえば任意の値を4としたとき
では、baseDate
として 4 を指定する、ということになります。
(1) Array.prototype.sort を使う。
ご質問の並べ替えを Array.prototype.sort で実現しようとする場合、たとえば baseDate
が 4
であるときは、 4
から配列hoge
javascript
1const hoge = [ 2 { date:"7", status:"C" }, 3 { date:"6", status:"B" }, 4 { date:"5", status:"D" }, 5 { date:"4", status:"B" }, 6 { date:"3", status:"A" }, 7 { date:"2", status:"C" }, 8 { date:"1", status:"A" } 9];
の各要素の date
プロパティ (を整数に変換した値) を引いた差の配列
javascript
1[-3, -2, -1, 0, 1, 2, 3]; // = [ 4-"7", 4-"6", 4-"5", 4-"4", 4-"3", 4-"2", 4-"1" ]
を、以下のように並び替えるにはどうしたらいいか? という問題に帰着します。
javascript
1[0, 1, 2, 3, -3, -2, -1];
上記のようなソートを行うための比較関数 compare(a, b)
の一例が以下です。(他のより良い書き方もあるかもしれません。)
javascript
1const compare = (a, b) => { 2 3 // a または b が 0 である場合 4 if ( a === 0 ) return -1; 5 if ( b === 0 ) return 1; 6 7 // a と b が異符号の場合 8 if ( a * b < 0 ) return b; 9 10 // a と b が同符号の場合 11 return a - b; 12 13};
上記のロジックを言葉で説明すると、 二つの整数 a
、 b
の比較関数 compare(a, b)
は、通常ではただ単に a - b
を返せば済むところを、このご質問の要件を満たすためには、 a
またはb
が 0 である場合の処理と、 a
と b
とが異符号である場合の処理を、 a - b
を返す前に行う必要がある、ということになります。
この compare(a,b)
を使うことで、ソート対象の配列 ary
(hoge が与えられる想定) と、 baseDate
を引数として取り、望ましい順序で並べ替えた配列を返す関数 sortByBaseDate(ary, baseDate)
を以下のように書けます。
javascript
1const sortByBaseDate = (ary, baseDate) => 2 ary.sort((e1,e2) => compare(baseDate - e1.date, baseDate - e2.date));
このように、 compare
と sortByBaseDate
を用意しておくと、 sortByBaseDate(hoge, 4)
によって、hoge
が望ましい状態に並び替えられます。
- 動作確認用のサンプル: https://jsfiddle.net/jun68ykt/6zqrL49a/1/
なお上記の比較関数によるソートは、 与えられる配列hoge
が、 date
でソートされていなくても望む結果が得られます。たとえば hoge
が
javascript
1const hoge = [ 2 { date:"5", status:"D" }, 3 { date:"2", status:"C" }, 4 { date:"6", status:"B" }, 5 { date:"4", status:"B" }, 6 { date:"1", status:"A" }, 7 { date:"3", status:"A" }, 8 { date:"7", status:"C" }, 9];
であった場合のサンプルが以下です。
hoge
**が **date
でソートされていない例:https://jsfiddle.net/jun68ykt/6zqrL49a/2/
(2) 配列の回転(rotate)を使う
上記の回答(1) は、初めに与えられる配列hoge
が date でソートされていなくても望ましい結果を得るための回答でしたが、前提条件としてhoge
の要素はdate
の降順にソートされているとするならば、配列 hoge
を回転(rotate)させた配列を作るにはどうしたらいいか?という問題と考えることができます。
配列を回転(rotate)させるためのコードは、以下の質問の回答に挙げられています。
- stackoverflow.com: JavaScript Array rotate()
これのベストアンサーになっている
https://stackoverflow.com/a/1985471
の Array.prototype.rotate をお借りすることにして、たとえば、baseDate
が 4 の場合は、hoge
の要素の中で date が "4"である要素を先頭にもってくるために、3回 rotate すればよいので、
javascript
1const rotateByBaseDate = (ary, baseDate) => [...ary].rotate(ary.length - baseDate);
を作っておいて、 rotateByBaseDate(hoge, 4)
とすれば望む配列が得られます。
以下、サンプルを作成しました。
- ** 配列をrotateさせるサンプル: ** https://jsfiddle.net/jun68ykt/tycq3g28/8/
上記のコードでは、初めに baseDate
を 4 として並べ替え、次に 5 で並べ替えます。
なお、[...ary]
としているところを、単に ary
としてしまうと、 rotateByBaseDate(hoge, 4)
としたときに hoge
自体が変更されてしまって、引き続き、 rotateByBaseDate(hoge, 5)
としたときに、望ましい結果にならなくなってしまうので、これを避けるために[...ary]
として ary
のコピーに対して Array.prototype.rotateを使っています。
以上、参考になれば幸いです。
追記1
長々と 回答(1)、(2)を書いてしまいましたが、もう少し見通しのいいコードで書けたので以下に追記します。
配列 hoge
javascript
1const hoge = [ 2 { date:"7", status:"C" }, 3 { date:"6", status:"B" }, 4 { date:"5", status:"D" }, 5 { date:"4", status:"B" }, 6 { date:"3", status:"A" }, 7 { date:"2", status:"C" }, 8 { date:"1", status:"A" } 9];
に以下のようなメソッドを追加します。
hoge.orderBy = function(baseDate) { const that = [...this]; if ( 1 <= baseDate && baseDate < this.length ) for ( i = 0; i < this.length - baseDate; ++ i ) that.push(that.shift()); return that; };
この orderBy
を使うことで、たとえば baseDate
に 4 を指定して並べ替えた配列 hoge4
を得るには、
javascript
1const hoge4 = hoge.orderBy(4);
とすればよいです。このorderBy
は、 hoge
の順序は変更しないので、続けて
javascript
1const hoge2 = hoge.orderBy(2);
などとしても、望ましい結果が返ります。
以下、この orderBy
を使ったサンプルです。
https://jsfiddle.net/jun68ykt/tycq3g28/15/
追記2
上記、追記1 の orderBy
は、以下のようにも書けます。
javascript
1hoge.orderBy = function(baseDate) { 2 3 if ( 1 <= baseDate && baseDate < this.length ) 4 return this.slice(this.length - baseDate) 5 .concat(this.slice(0, this.length - baseDate)); 6 7 return [...this]; 8 9};
**サンプル: ** https://jsfiddle.net/jun68ykt/tycq3g28/16/
上記の slice
と concat
を使った orderBy
で事足りるということだと、sort させたり、 rotateさせたりといった込み入ったことをする必要はありませんでした。あとは、もともと hoge
が date でソートされているかどうか、といった要件しだいで、お好みの方法を比較検討して頂ければと思います。
投稿2018/09/14 21:05
編集2018/09/16 01:34総合スコア9058
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
十分良い方法とは言えない(※)かもしれませんが・・・
以下の想定をするならArray.prototype.sortの比較関数をひとひねりするとそういうことができると思います。
<想定>
・dateの値の範囲が既知
・配列の要素数はそれほど大きくない(ソートキーを動的に計算することのオーバーヘッドは気にしない)
<方針>
dateを基準値によって望む順番となるようなソートキー(数値)に変換しそれに基づきソートする。
例えば、
(A) dateが基準値以下の場合はソートキーを- date
とする
(B) dateが基準値を超える場合はソートキーを十分大きな値 - date
とする。
要するに後の方へ並べたい範囲(dateが基準値より大きな要素)に対してソートキーにゲタをはかせて大きなキー値となるようにするわけです。
js
1// 基準日を与えてArray.prototype.sort用の比較関数を返す 2function myComparator(base_date) { 3 const BIG_ENOUGH = 999999 4 5 return (a, b) => toKey(a) - toKey(b) 6 7 function toKey(item) { 8 const v = +item.date // 文字列->数値 9 const r = (v <= base_date ? 0 : BIG_ENOUGH) - v 10 // console.log('v = ' + v + ', r = ' + r) 11 return r 12 } 13}
※: 十分良い方法とは言えないと思った根拠
「dateの値の範囲を前提としてゲタの値(BIG_ENOUGH)を決め打ちしている」点です。場合によってはこういうマジックナンバーをコードに混入させるのは分かりやすさやアルゴリズムの頑強性を損なってしまうと思います。質問文をみるとdateはdate of the weekのようなもの、つまり「曜日」を表しているようにも見えます。もしそうならBIG_ENOUGHを決め打ちしても問題ないでしょう。しかしながらdateがある基準日からの経過日数であり、最大の値を特に想定しないのであればBIG_ENOUGHは実用上問題がない程度の十分大きな値を注意深く決めなければならないでしょう。
古い話になりますが2000年問題というのが過去にありました。それまで計算機の年号を西暦の下2桁で表現するプログラムが世の中にたくさん存在していたため1999->2000年になったとたん日付の前後関係がトンデモナイことになり「ヤバイ!」となったという故事です。あちこちのソフトウェア提供者・利用者が既存プログラムの改修にテンテコマイになったらしいです・・・
上のコードのBIG_ENOUGHもそういうたぐいの問題になりえるため、たとえ十分大きな値を想定できたとしても「筋が悪い」「気持ち悪い」と感じるプログラマーが多いかもしれません。その点でhope_mucciさん回答は頑強であると思います。自分はなるべく頑強なコードを心がけはしますが、アマチュアのため自分個人が使うプログラムでは本回答のような妥協も多いです。
確認環境:
nodejs v8.12.0
JavaScriptの仕様のバージョンはECMAScript2015を想定
参照:
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
蛇足:
キーの重複やソートの安定性についての配慮については質問意図から外れると思ったので端折っています。ソートの基本ですのでもしご存じないなら別途調べておくことをお勧めします。
投稿2018/09/14 20:37
総合スコア18394
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
アイディアとしては、「任意の値を境にして配列を分割し、前後を入れ替える」でしょう。
- 任意の値はひとまず置いておいて配列を降順で並び変える
- 任意の値が入っている要素の位置を特定する。質問文の例なら4番目。
- 任意の値以降の要素が全部入った配列と任意の値の前の配列に分割する。
質問文の例なら「4番目以降残り全部」の配列と「1番目から3番目まで」の配列に分割。
Array.sliceを使うとてっとり早いでしょう。
var hoge1 = hoge.slice(3); var hoge2 = hoge.slice(0,3);
- 「4番目以降残り全部の配列」、「1番目から3番目まで」の順で配列を連結する。
Array.concatを使いましょう。
hoge1.concat(hoge2);
投稿2018/09/14 16:30
総合スコア4447
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
2 つの方法で書いてみました。
s.node
node
1var data = [ 2 {date:"4", status:"B"}, 3 {date:"3", status:"A"}, 4 {date:"2", status:"C"}, 5 {date:"1", status:"A"}, 6 {date:"7", status:"C"}, 7 {date:"6", status:"B"}, 8 {date:"5", status:"D"} 9]; 10console.log(data); 11 12start_date = '7'; 13 14// date 自身をローテートしていく 15while(data[0].date != start_date) { 16 data.push(data.shift()); 17} 18console.log(data); 19 20// date => status の map を作る。 21// date 情報の配列をローテートする。 22// data 情報の配列に従って、 map から配列を作る 23map_status = new Map; 24array_date = [] 25for (var x of data) { 26 map_status[x.date] = x.status; 27 array_date.push(x.date); 28} 29// console.log(map_status); 30// console.log(array_date); 31 32while(array_date[0] != start_date) { 33 array_date.push(array_date.shift()); 34} 35result = [] 36for(var date of array_date) { 37 result.push({date: date, status: map_status[date]}); 38} 39console.log(result);
上では、 '7' が最初にくるようにしたものを得るようにしています。(start_date で指定しています)
最初の方法は、 質問文にある配列を、 指定された data が先頭にくるまで、ローテートしていっています。
もう一つの方法は、 まずデータの持ち方を変えています。
質問文では、 map の array になってますが、これを date => status の map にします。
最終的に取得したい配列は、 この date => status の map から順番に取り出すようにして作ることにします。
そうすると問題は 取り出す順番を指定する date の配列をどうやってつくるかに還元されます。
最初は date の配列は、 最初は質問文のデータから date の値から作ります。
そして、指定された date が先頭にくるまでローテートさせています。
投稿2018/09/16 20:54
総合スコア22324
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。