質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

6回答

1283閲覧

配列を任意の値で並び替え

eeengineeeeeeer

総合スコア15

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2018/09/14 16:14

編集2018/09/14 16:14

下記のような配列を任意の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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kei344

2018/09/14 16:18

ご自身で試されたコードを質問文に追記し、「何」が「どのように」わからないのか、コードのどの部分で詰まっているのかなどを具体的に追記されたほうが回答が望めると思います。
a_saitoh

2018/09/15 06:07

dateの最大値最小値は決まっていますか?
a_saitoh

2018/09/15 07:53

意図するのは「dateが4未満の要素を並べた後に、dateが4以上の要素」でしょうか?「dateが4以下の要素を並べた後に、dateが4を越える要素」でしょうか?
guest

回答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

think49

総合スコア18164

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

javascriptにもともとあるarray.sortを使い、渡す比較関数を自分で書けばOKでしょう。
例えば名前をcompare(a,b)として
aとbが同列なら0
aのほうがbより先に来るように並べ替えたいなら -1
aのほうがbより後に来るように並べ替えたいなら +1
を返すように比較関数を書きます。

投稿2018/09/16 01:25

a_saitoh

総合スコア702

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KSwordOfHaste

2018/09/16 12:45

自分は難しく考えすぎてました...ソートキーへの写像を下手に考えたため境界条件を気にする必要があるかのように考えてしまいましたが、直接比較論理を書けば普通に明快な論理になりますよね・・・
guest

0

ベストアンサー

こんにちは。

このご質問でeeengineeeeeeerさんが要望されている並べ替えを実現するために、異なる2つのアプローチがあると思いましたので、それぞれ以下の(1)、(2)として回答します。

なお、以下の回答では、並べ替えた結果、先頭にくるべき要素のdate プロパティに相当する整数値を、 baseDate という変数名で表します。ご質問の例:

たとえば任意の値を4としたとき

では、baseDateとして 4 を指定する、ということになります。

(1) Array.prototype.sort を使う。

ご質問の並べ替えを Array.prototype.sort で実現しようとする場合、たとえば baseDate4 であるときは、 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};

上記のロジックを言葉で説明すると、 二つの整数 ab の比較関数 compare(a, b) は、通常ではただ単に a - b を返せば済むところを、このご質問の要件を満たすためには、 aまたはbが 0 である場合の処理と、 abとが異符号である場合の処理を、 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));

このように、 comparesortByBaseDate を用意しておくと、 sortByBaseDate(hoge, 4) によって、hoge が望ましい状態に並び替えられます。

なお上記の比較関数によるソートは、 与えられる配列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];

であった場合のサンプルが以下です。

(2) 配列の回転(rotate)を使う

上記の回答(1) は、初めに与えられる配列hoge が date でソートされていなくても望ましい結果を得るための回答でしたが、前提条件としてhogeの要素はdateの降順にソートされているとするならば、配列 hoge を回転(rotate)させた配列を作るにはどうしたらいいか?という問題と考えることができます。

配列を回転(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) とすれば望む配列が得られます。
以下、サンプルを作成しました。  

 上記のコードでは、初めに 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/

上記の sliceconcatを使った orderBy で事足りるということだと、sort させたり、 rotateさせたりといった込み入ったことをする必要はありませんでした。あとは、もともと hogeが date でソートされているかどうか、といった要件しだいで、お好みの方法を比較検討して頂ければと思います。

投稿2018/09/14 21:05

編集2018/09/16 01:34
jun68ykt

総合スコア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

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

アイディアとしては、「任意の値を境にして配列を分割し、前後を入れ替える」でしょう。

  1. 任意の値はひとまず置いておいて配列を降順で並び変える
  2. 任意の値が入っている要素の位置を特定する。質問文の例なら4番目。
  3. 任意の値以降の要素が全部入った配列と任意の値の前の配列に分割する。

質問文の例なら「4番目以降残り全部」の配列と「1番目から3番目まで」の配列に分割。
Array.sliceを使うとてっとり早いでしょう。

var hoge1 = hoge.slice(3); var hoge2 = hoge.slice(0,3);
  1. 「4番目以降残り全部の配列」、「1番目から3番目まで」の順で配列を連結する。

Array.concatを使いましょう。

hoge1.concat(hoge2);

投稿2018/09/14 16:30

hope_mucci

総合スコア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

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問