web-dev-qa-db-ja.com

プレーヤーのhtmlリストを並べ替える効率的なアルゴリズム

プレーヤーのリストがあり、次のような形式で表されています。

_<div class="players_list">
   <div class="player" data-points="500">
        <div class="name">Player1</div>
        <div class="position">1</div>
   </div>     
   <div class="player" data-points="400">
        <div class="name">Player2</div>
        <div class="position">2</div>
   </div>
   <div class="player" data-points="300">
        <div class="name">Player3</div>
        <div class="position">3</div>
   </div>
   <div class="player" data-points="200">
        <div class="name">Player4</div>
        <div class="position">4</div>
   </div>  
</div> 
_

何かが起こり、プレーヤー4は600ポイントを受け取ります。だから1位になります。彼と一緒に-player3は300ポイントを受け取り、2位になります。

それは同じイベントの後に起こります-それらがサーバーからこれらのポイントを取得すると仮定しましょう、しかしそれはサーバーで許可されているすべての負荷であり、サーバーは位置を計算しません。

このようなビューを取得する最も効率的な方法は何ですか?

_<div class="players_list">
   <div class="player" data-points="800">
        <div class="name">Player4</div>
        <div class="position">1</div>
   </div>
   <div class="player" data-points="700">
        <div class="name">Player3</div>
        <div class="position">2</div>
   </div>

   <div class="player" data-points="500">
        <div class="name">Player1</div>
        <div class="position">3</div>
   </div>     
   <div class="player" data-points="400">
        <div class="name">Player2</div>
        <div class="position">4</div>
   </div>
</div> 
_

それはコードについての問題ではなく、以下についての問題です。

  • それはどんな仕事ですか?並べ替えるだけ?
  • リストが500行未満の場合、JavaScriptを使用してこのタスクを解決しても問題ありませんか?
  • プレーヤーのリスト全体を再レンダリングしてもいいですか、それともdiv要素の位置(DOMのインデックスなど)を変更するだけの方法がありますか?
  • このタスクを効率的に解決するのに役立つツールやライブラリはありますか?

PS:このストーリーをすべてアニメーション化したい場合は、すべてのプレーヤーのスコアが再計算されるたびにアニメーションタスクのキューを作成する必要がありますか?

更新:

私は@Jalaynの答えに従って、次の問題にこだわりました:

リスト_[1,2,3,4]_が_[2,3,4,1]_に変換するとします。マップすると、次の位置の差分_[+1, +1, +1, -3]_を取得します。要素を1つずつその位置の変更に移動すると、位置の差分を交渉するまで、すべてが正常に行われます-最初の位置の差分ができる ' tもう下に移動します。

それで、質問への私の更新-行を2回以上アニメーション化するにはどうすればよいですか?

そして私が行き詰まっている最後の問題:

それがこの質問で最も解決されるかどうかはわかりません。ところで、2人のプレーヤーが同じポイントを持っている場合-アニメーションプランブレーキとelement_idsが重複しています。

私の質問は、構造リストの次の変換をどのように行うかです:[(#id1, 1, 100),(#id2, 2, 50), (#id3, 2, 50), (#id4, 4, 25)] => [(#id3, 1, 150), (#id1, 2, 100), (#id4, 3, 75), (#id2, 4, 50)]

今のところ、私の解決策はリスト全体を更新することですが、完全に再生成することはありません。すべての要素を最初の位置に移動し、バッキングされたJSONで並べ替えます。アニメーションは複雑すぎて最終的な問題の原因にはなりません。

5
Nikolay Fominyh

ええと、私はあなたのすべての質問に答え、十分に正確になるように願っています。

すべての質問に対する答えは「はい」です。JavaScriptでそれをすべて行うことが可能です。

それはどんな仕事ですか?並べ替えるだけ?

データを並べ替える必要がありますが、一部のデータ(ポイント、名前?、位置)を更新してユーザーに表示する必要もあります。

リストが500行未満の場合、JavaScriptを使用してこのタスクを解決しても問題ありませんか?

JavaScriptを使用して500行で構成されるテーブルを再配置できます( JQueryを少し使用して )。これでパフォーマンスの問題が発生することはないと思います。発生する可能性がある唯一の問題は、500の移動する行をアニメーション化しようとした場合です(詳細は後ほど)。

プレーヤーのリスト全体を再レンダリングしてもいいですか、それともdiv要素の位置(DOMのインデックスなど)を変更するだけの方法がありますか?

ええと、プレイヤーリスト全体を再レンダリングすることは問題ないと思いますが、そうする場合、位置変更アニメーションをどのように実行しますか?

このタスクを効率的に解決するのに役立つツールやライブラリはありますか?

明白な答え:JQueryおよびJQuery UI(またはアニメーションを実行するためのお気に入りのJavaScriptフレームワーク)

このストーリーをすべてアニメーション化したい場合、すべてのプレイヤーのスコアが再計算されるたびにアニメーションタスクのキューを作成する必要がありますか?

各プレイヤーの最終的な位置を把握した上で、アニメーションを一つずつ実行していくべきだと思います。それ以外の場合、何も変更されていないときにアニメーションを実行できます(player3が3から1に移動し、player2が2から1に移動し、player1が1から... 1に移動した場合、何も変更されません!)

DOMのみvs JSONデータモデルに基づくDOM

ここで、「バックエンド」JSONデータモデルを使用せずにDOM要素のみを使用する場合は、使用するツールやアルゴリズムに関係なく(さらに問題が発生した場合は、ここに記入してください):

  • 特定のプレイヤーの新しいポジションを見つけるのは面倒です
  • すべてのプレイヤーのポジションを更新するのは面倒です

JSONデータモデルに裏打ちされたレンダリングのためにDOM要素を操作し、個々のプレーヤーの表示に次の変更を加える場合、時間ははるかに簡単になります。

   <div class="player" id="player2">
        <div class="name">Player2</div>
        <div class="points" id="playerPoints2">400</div>
        <div class="position" id="playerPosition2">2</div>
   </div>
  • プレイヤーにid属性を追加したことに注意してください<div>と、ポイント( "playerPoints2")と位置( "playerPosition2")を表示する要素。そうすることで、一意のIDを指定して、プレーヤーの位置またはそのポイントをすばやく見つけることができます。なくてもかまいませんが、パフォーマンスに影響し、より複雑なクエリを使用する必要があります(JQueryの 。find() を参照)

私の試み:

要するに、私がやろうとしていることは、できるだけ少ないDOM要素を移動し、表示からデータを非相関化することです。

プレーヤーを表示するためのDOMモデルに加えて、次のJSONデータモデルがあるとします。

  • プレーヤー(プレーヤーの配列)
    • id
    • 位置(配列の各項目の場合、「位置」属性を指定して配列をソートするための カスタム関数 を記述できます)
    • ポイント
    • 名前

後でアニメーションを実行するための次のJSONデータモデル:

  • playerChanges(プレーヤーの変更の配列)
    • id
    • deltaPosition(例:+3 =以前より3ランク高い)。 * isComputed(ブール値)

サーバーからデータを受信した後、私が行うことは次のとおりです。

  • それぞれの位置を指定して、バックエンドプレーヤーのリストを並べ替えます。配列の最初の要素は、最上位のプレーヤーでなければなりません。
  • サーバーから受信したデータをループする
  • プレーヤーのポイントが変化した場合:
    • 配列内のプレーヤーを検索します
    • ポイントを更新する
    • プレーヤーを配列内の適切な位置に移動しますが、「位置」属性は変更しないでください
    • 「playerChanges」配列に新しいエントリを作成し、プレーヤーのIDのみを保存します(最後にのみデルタを計算します)
  • バックエンドプレーヤーの配列でループし、まだ計算されていない "playerChanges"(これは重要です)の各プレーヤーについて、配列内のプレーヤーの位置に関する位置属性を計算します。彼の「古い」位置と配列内の位置があるので、「deltaPosition」を計算できます。 編集:デルタを計算した後、「playerChange」を計算済みとしてマークし、バックエンドプレーヤーの配列でループを再開します。配列に計算されていない「playerChange」がある限り、それを行います。

最後に、「playerChanges」の配列が必要です。これは、各プレーヤーの「deltaPosition」を指定した場合に移動する必要がある行のリストです。 0になる可能性があることに注意してください。これは、プレーヤーが新しいポイントを受け取ったが、彼の位置は変化しなかったことを意味します。アニメーションを実行するには、 この素晴らしい答え を見ることをお勧めします。

これらのアニメーションを実行した後、バックエンドプレーヤーのリストで再びループして、各プレーヤーの表示位置( "playerPositionXXX"、ここでXXXはプレーヤーのID)を更新できます。

ここで私が試みたのは、画面上で発生すること、つまりユーザーに表示されるアニメーションから「更新とソート」を非相関にすることです。

編集:これは、JQueryとJQueryUIを使用したアニメーションの(Chrome少なくとも)の)動作するコードサンプルです

<html>
    <head>
        <title>Temp test</title>
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
        <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.min.js"></script>
        <style type="text/css">
            * { font-family: Calibri;}
            .player { border: 1px solid grey; background: #f0f0f0; width: 300px; 
                        padding: 10px; margin-bottom: 5px; margin-top: 5px; }
                .name { margin-left: 30px; font-size: 20px; font-weight: bold; }
                .points { float: right; color: whitesmoke; font-size: 24px; font-weight: bold; margin-top: -30px; 
                            background-color: #999; padding: 3px;}
                .position { clear: both; display: block; float: left; 
                            font-size: 20px; font-weight: bold; margin-top: -29px; padding-right: 10px; 
                            border-right: 1px solid black;}
        </style>
        <script type="text/javascript">

            function movePlayer(playerToMoveId, delta) {
                var $old = $(playerToMoveId);
                var $new = $old.clone();

                // TODO: here I don't take into account the delta
                // 
                // you need to write a simple method for determining the player
                // that will be replaced (here I hardcoded "p4"). 
                $("#p4").before($new);

                var newOffset = $new.offset();
                //Get the old position relative to document
                var oldOffset = $old.offset();
                //we also clone old to the document for the animation
                var $temp = $old.clone().appendTo('body');
                //hide new and old and move $temp to position
                //also big z-index, make sure to edit this to something that works with the page
                $temp
                  .css('position', 'absolute')
                  .css('left', oldOffset.left)
                  .css('top', oldOffset.top)
                  .css('zIndex', 1000);
                $new.hide();
                $old.hide();
                //animate the $temp to the position of the new img
                $temp.animate( {'top': newOffset.top, 'left':newOffset.left}, 'slow', function(){
                   //callback function, we remove $old and $temp and show $new
                   $new.show();
                   $old.remove();
                   $temp.remove();
                });
            }
        </script>
    </head>
    <body>
        <div class="players_list" id="plist">
           <div class="player" id ="p4">
                <div class="name" id="pName4">Player4</div>
                <div class="points" id="pPts4">800</div>
                <div class="position" id="pPos4">1</div>
           </div>
           <div class="player" id="p3">
                <div class="name" id="pName3">Player3</div>
                <div class="points" id="pPts3">500</div>
                <div class="position" id="pPos3">2</div>
           </div>

           <div class="player" id="p1">
                <div class="name" id="pName1">Player1</div>
                <div class="points" id="pPts1">300</div>
                <div class="position" id="pPos1">3</div>
           </div>     
           <div class="player" id="p2">
                <div class="name" id="pName2">Player2</div>
                <div class="points" id="pPts2">250</div>
                <div class="position" id="pPos2">4</div>
           </div>
        </div> 

        <input type="button" onclick="movePlayer($('#p2'), 3)" value="Animate"/>
    </body>
</html>
8
Jalayn

さまざまなシステムといくつかのタイプのホスト(node.jsの可能性があります)上にマルチプレーヤーがあると仮定します。

これは典型的なupdate-score eventだと思います。サーバーでトリガーされるもの。スコアが変化すると、サーバーは新しい(ランク付けされた)スコアを各プレーヤーに送信する必要があります。

クライアントサイドでrenderingスコアリストにjavascript(jquery)を使用します。 HTML5を使用すると、Webソケットを使用して、サーバー(計算が完了)がイベントを送信すると同時に新しいスコアをストリーミングできます(または単に新しいスコアリストが利用可能であることをクライアントに通知します)。

私はサーバー(MVC)に(可能な限り合理的な)できるだけ多くのロジックを保持します。

1
Roger