web-dev-qa-db-ja.com

数値を降順で並べ替えますが、先頭は「0」です

私はJavaScriptの問題を抱えており、しばらくの間、もう少し理解しようとしています。

この配列を考えてみましょう:

let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

この結果を出力する必要があります:

arr = [0, 0, 0, 0, 0, 5, 4, 3, 2, 1]

次のロジック行に従って、ゼロを前に配置し、インデックス値を調整します。

arr.sort((x, y) => {
    if (x !== 0) {
        return 1;
    }

    if (x === 0) {
        return -1;
    }

    return y - x;
});

しかし、私はこの結果に行き詰まっています。

arr = [0, 0, 0, 0, 0, 1, 2, 3, 4, 5]

これを解決するためのヒントはありますか?

36
lianbwl

baのデルタでソートして(降順ソートの場合)、 Number.MAX_VALUE 、ゼロのような偽の値の場合。

この:

Number.MAX_VALUE - Number.MAX_VALUE

ゼロに等しい。

let array = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

array.sort((a, b) => (b || Number.MAX_VALUE) - (a || Number.MAX_VALUE));

console.log(...array);
35
Nina Scholz

mdn docsが言うように:

Aとbが比較される2つの要素の場合、次のようになります。

compareFunction(a, b)が_0_未満を返す場合、ab未満のインデックスにソートします(つまり、aが最初に来ます)。

compareFunction(a, b)が0を返す場合、aとbは互いに変更せずに、すべての異なる要素に関してソートされます。注:ECMAscript標準はこの動作を保証するものではないため、すべてのブラウザー(たとえば、少なくとも2003年まで遡るMozillaバージョン)がこれを尊重するわけではありません。

compareFunction(a, b)が0より大きい値を返す場合、bをaより小さいインデックスにソートします(つまり、bが最初に来ます)。

abの2つの引数として特定の要素のペアを指定した場合、compareFunction(a, b)は常に同じ値を返す必要があります。一貫性のない結果が返される場合、ソート順は未定義です。

したがって、比較関数は次の形式になります。

_function compare(a, b) {
  if (a is less than b by some ordering criterion) {
    return -1;
  }
  if (a is greater than b by the ordering criterion) {
    return 1;
  }
  // a must be equal to b
  return 0;
}
_
_let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

arr.sort((x, y) => {
    if (x > 0 && y > 0) {
        return y - x;
    }
    return x - y;
});

console.log(arr);_
24
StepUp

効率を重視する場合は、最初にゼロを除外するのがおそらく最速です。あなたはsortがそれらを見ても時間を無駄にしたくはありませんし、その特別なケースを処理するために比較コールバックに追加の作業を追加することは言うまでもありません。

特に、かなりの数のゼロが予想される場合、データを1回通過させてフィルターで除外する方が、各ゼロを複数回参照するより大きなO(N log N)ソートを実行するよりもはるかに優れています。

あなたがすることができます 効率的に 完了した後、適切な数のゼロを前に付加します。

結果のコードを読むのも同じくらい簡単です。 TypedArrayを使用したのは、効率的で 数値の並べ替えが容易になる だからです。ただし、_(a,b)=>a-b_の_.sort_の標準イディオムを使用して、通常の配列でこの手法を使用できます。

_let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

let nonzero_arr = Int32Array.from(arr.filter(n => n != 0));
let zcount = arr.length - nonzero_arr.length;
nonzero_arr.sort();      // numeric TypedArray sorts numerically, not alphabetically

// Reverse the sorted part before copying into the final array.
nonzero_arr.reverse();

 // efficient-ish TypedArray for main result
let revsorted = new Int32Array(arr.length);   // zero-filled full size
revsorted.set(nonzero_arr, zcount);           // copy after the right number of zeros

console.log(Array.from(revsorted));      // prints strangely for TypedArray, with invented "0", "1" keys

/*
   // regular Array result
let sorted = [...Array(zcount).fill(0), ...nonzero_arr]  // IDK if this is efficient
console.log(sorted);
*/_

TypedArray .sort()の次に_.reverse_がカスタム比較関数を使用して降順で並べ替えるよりも速いかどうかはわかりません。または、イテレータを使用してオンザフライでコピーおよびリバースできる場合。


検討する価値もあります:全長のTypedArrayを1つだけ使用してください。

_.filter_を使用する代わりに、ループし、swapゼロを配列の前に移動します。これは、データを1回通過します。

次に、 .subarray() を使用して、同じ基になるArrayBufferのゼロ以外の要素の新しいTypedArrayビューを取得します。並べ替えでは、開始点がゼロで末尾が並べ替えられた完全な配列が残ります。並べ替えでは、ゼロ以外の要素のみが表示されます。

ArrayまたはTypedArrayメソッドにパーティション関数は表示されませんでしたが、JavaScriptはほとんど知りません。優れたJITがあれば、ループが組み込みメソッドよりも悪くなることはありません。 (特にそのメソッドが_.filter_のようなコールバックを含む場合、縮小するために内部でreallocを使用しない限り、実際にフィルタリングする前に割り当てるメモリ量を把握する必要があります)。

TypedArrayに変換する前に、regular-Array .filter()beforeを使用しました。入力がすでにTypedArrayである場合、この問題は発生しないため、この戦略はさらに魅力的になります。

13
Peter Cordes

このように比較関数の条件を変更するだけです-

let arr = [-1, 0, 1, 0, 2, -2, 0, 3, -3, 0, 4, -4, 0, 5, -5];
arr.sort((a, b) => {
   if(a && b) return b-a;
   if(!a && !b) return 0;
   return !a ? -1 : 1;
});

console.log(arr);
8
Harunur Rashid

ここでコードゴルフをしない:

let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, -1];
arr.sort(function(a, b) {
  if (a === 0 && b !== 0) {
    // a is zero b is nonzero, a goes first
    return -1;
  } else if (a !== 0 && b === 0) {
    // a is nonzero b is zero, b goes first
    return 1;
  } else {
    // both are zero or both are nonzero, sort descending
    return b - a;
  }
});
console.log(arr.toString());
6
Salman A

すでに存在する場合は、独自の数値ソートを記述しないでください。あなたがしたいことはまさにあなたがタイトルで言うことです。先頭のゼロを除いて、降順で数値を並べ替えます。

const zeroSort = arr => [...arr.filter(n => n == 0),
                         ...new Float64Array(arr.filter(n => n != 0)).sort().reverse()];

console.log(zeroSort([0, 1, 0, 2, 0, 3, 0, 4, 0, 500]));

不要なコードは記述しないでください。あなたはそれを誤解するかもしれません。

配列で処理する数値のタイプに基づいて TypedArray を選択します。 Float64は、すべての通常のJS番号を処理するため、適切なデフォルトです。

5
JollyJoker

これは次のようにして行うことができます:

let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

let result = arr.sort((a,b) => {
  if(a == 0 || b == 0)
    return a-b;
  return b-a;
})
console.log(result)

またはこれを行うことができます:

let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

let result = arr.sort().sort((a,b) => {
  if(a > 0 && b > 0)
    return b-a
  return 0
})

console.log(result)
4
NuOne