JavaScript - 處理一些排序的方式
on 2020-11-18
常常會遇到用 JavaScript 處理一些資料結構的排序
這裡會列舉一些使用情境與案例
取出排名前幾名的資料(排名的值不重複)
資料
var data = {
123: {
count: 123,
type: "video",
source: "",
},
345: {
count: 345,
type: "video",
source: "",
},
99: {
count: 99,
type: "image",
source: "",
},
1: {
count: 1,
type: "video",
source: "",
},
9786: {
count: 9786,
type: "image",
source: "",
},
347: {
count: 347,
type: "video",
source: "",
},
};
處理方式
function topNine(data) {
var arr = [];
(tmp = Object.keys(data).reverse()), (l = tmp.length >= 9 ? 9 : tmp.length);
for (let i = 0; i < l; i++) {
arr.push(data[tmp[i]]);
}
return arr;
}
處理範例為取出前 9 筆
這樣的處理是使用 Object 的 Key 的排序本身是升冪排序
為了讓代碼好讀懂
使用了 Object.keys(data).reverse()
反轉陣列使其變成降冪排序
這樣跑 for 迴圈就可以自然而然的從 0 開始
考慮到資料可能不到 9 筆
所以用 (tmp.length >= 9) ? 9 : tmp.length
來處理迴圈要跑的次數
這樣這個函數就可以回傳前 9 名的資料了
但是這樣的資料結構有個問題就是範例資料的 count 必須是唯一值, 因為 JSON Object 不能重複 key
取出排名前幾名的資料(排名的值重複)
資料
var data = [
{
source: "",
type: "image",
ts: 1600860409000,
liked: 9680,
},
{
source: "",
type: "image",
ts: 1603438465000,
liked: 11746,
},
{
source: "",
type: "image",
ts: 1602389743000,
liked: 13289,
},
{
source: "",
type: "image",
ts: 1605334079000,
liked: 14095,
},
{
source: "",
type: "image",
ts: 1602652312000,
liked: 14138,
},
{
source: "",
type: "image",
ts: 1603275324000,
liked: 14310,
},
{
source: "",
type: "image",
ts: 1603600329000,
liked: 14448,
},
{
source: "",
type: "image",
ts: 1603080713000,
liked: 14625,
},
{
source: "",
type: "image",
ts: 1604823132000,
liked: 15351,
},
{
source: "",
type: "image",
ts: 1604919156000,
liked: 15373,
},
{
source: "",
type: "image",
ts: 1601123545000,
liked: 15442,
},
{
source: "",
type: "image",
ts: 1601544281000,
liked: 16659,
},
{
source: "",
type: "image",
ts: 1605088396000,
liked: 17137,
},
{
source: "",
type: "image",
ts: 1602749094000,
liked: 17483,
},
{
source: "",
type: "image",
ts: 1600928882000,
liked: 17928,
},
{
source: "",
type: "image",
ts: 1602562980000,
liked: 18217,
},
{
source: "",
type: "image",
ts: 1603709259000,
liked: 21845,
},
{
source: "",
type: "image",
ts: 1602470351000,
liked: 22292,
},
{
source: "",
type: "image",
ts: 1603887487000,
liked: 22559,
},
{
source: "",
type: "image",
ts: 1602846057000,
liked: 22824,
},
{
source: "",
type: "image",
ts: 1601465442000,
liked: 25226,
},
{
source: "",
type: "image",
ts: 1604736681000,
liked: 25580,
},
{
source: "",
type: "image",
ts: 1601285690000,
liked: 25796,
},
{
source: "",
type: "image",
ts: 1601693594000,
liked: 26078,
},
];
處理方式
第一種解法
function topNine(data) {
var arr = [];
var i, j, temp;
for (i = 0; i < data.length - 1; i++) {
for (j = 0; j < data.length - 1 - i; j++) {
if (data[j]["liked"] < data[j + 1]["liked"]) {
temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
var l = data.length >= 9 ? 9 : data.length;
for (var n = 0; n < l; n++) {
arr.push(data[n]);
}
return arr;
}
這個也不太難理解
第一個迴圈依照 liked 跑了一個降冪的氣泡排序(一般情況都是跑升冪)
temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
這邊使用 variable swap 的技巧來做前後兩個值的交換
c = a;
a = b;
b = c;
第二個迴圈就是取出前 9 筆的 liked 數
一樣是在 data.length >= 9 ? 9 : data.length
確認未滿 9 筆的話就有多少取多少
第二種解法
因為資料是 Array
所以可以使用 sort
來處理
function topNine(data) {
var arr = [];
data = data.sort(function (a, b) {
return b.liked - a.liked;
});
var l = data.length >= 9 ? 9 : data.length;
for (var n = 0; n < l; n++) {
arr.push(data[n]);
}
return arr;
}
sort
可以帶入 function 處理回傳的比較的參數
所以可以回傳物件的值得比較結果
如果預設升冪排序就用 a - b
降冪排序就用 b - a
HTML form submit same name in php, nodejs, golang
on 2020-10-24
一直以來大部分時間都在用 PHP 開發
所以也用 PHP 來處理 HTML Form
所以都下意識地認為 <input type="checkbox" name="game[]" value="FGO">
這樣的 name="game[]"
的處理方式是正規的處理 HTML Form 的多選的方式
也疑惑為何大多的 HTML Form 的教學甚至 MDN 都沒有提到這件事
就在某一天我在檢視到同事寫的 code 時
發現同事用 JavaScript 處理
硬爬出來自己組字串送出去
我才想起這令人感到恐懼的事情
因為公司同事是寫 golang 的專門, 就算前端不熟
應該也不至於連這樣概念都沒有就用硬爬的方式處理
所以再調整的同時也跟同事確認後
我也真正的直視這問題
到底要怎麼處理表單中多選的資料?
PHP 寫久的人大多都知道要用上面列出的方法
name="game[]"
就是 game
+ []
但是當我認真地尋找關於這個問題時
意外地發現了一篇 stackoverflow 的問答
Several Checkboxes sharing the same name
其實 W3C 根本沒有管你 name=”” 重複要如何處理
以下是 PHP, nodejs, golang 的原生方式來測試的結果
PHP
以下問答有提供了 PHP doc 說明 PHP 如何處理多選
在 name 後面添加 []
即可處理把相同的 name 組成 Array
沒有加的話是會取最後一個
POST & GET 都是如此處理
example:
<input type="checkbox" name="game[]" value="FGO" />
<input type="checkbox" name="game[]" value="GirlsFrontline" />
<?php
$_GET['game'];
$_POST['game'];
nodejs
GET 和 POST 用的方式不一樣
基本上都會用到 querystring
module
querystring
module 主要都在處理 querystring 相關的功能
主要靠它 parse querystring format(ex: key=calue&key2=value2)
所以 nodejs 會自動把同樣的 name 併在一起組成 array
GET
會需要用 url
module
url
module 主要是要拿掉 GET 的 url 的 querystring 再丟給 querystring
處理
example:
<input type="checkbox" name="game" value="FGO" />
<input type="checkbox" name="game" value="GirlsFrontline" />
const url = require("url");
const querystring = require("querystring");
const thisUrl = new URL(req.url, "http://" + req.headers.host);
const query = new URLSearchParams(thisUrl.search).toString(); // 這一段是要把 `?key=calue&key2=value2` => `key=calue&key2=value2`
querystring.parse(query)["game"];
POST
把接進來的 body 組起來再使用 querystring
parse
example:
var formData = "";
req.on("data", function (data) {
formData += data;
});
req.on("end", function () {
console.log(querystring.parse(formData)["game"]);
});
URL | Node.js v15.0.1 Documentation
Query string | Node.js v15.0.1 Documentation
golang
import http
就可以處理 GET 和 POST 的情況
大致上的處理方式和行為和 nodejs 一樣(只差在語言的寫法不一樣)
也是會自動把同樣的 name 併在一起
組成 slice
GET
request struct 裡面有 URL 有 function Query
可以拿到所有的 querystring
example:
<input type="checkbox" name="game" value="FGO" />
<input type="checkbox" name="game" value="GirlsFrontline" />
package main
import (
"fmt"
"net/http"
)
func getHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL.Query()["game"])
}
func main() {
http.HandleFunc("/get", getHandler)
}
POST
request 有一個 parseForm
的 function 可以抓取 HTTP POST
, PUT
, and PATCH
的 body
example:
package main
import (
"fmt"
"net/http"
)
func postHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Println(r.Form["game"])
}
func main() {
http.HandleFunc("/post", postHandler)
}
最後總結
只有 PHP 要在相同的 name 後面添加 []
才可以在 PHP 端收到的 name 併在一起
nodejs 和 golang 都只要是 name 一樣就可以了
針對這三種語言都有寫了 demo code
只要三種語言的環境裝好
都可以直接起 local server 來試驗看看