tedshd's DevNote

tedshd's DevNote

Develop & Design Note by Ted

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

refer - Array.prototype.sort()

Read more

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 如何處理多選

PHP Multiple Checkbox Array

在 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)
}

http - parseForm

最後總結

只有 PHP 要在相同的 name 後面添加 [] 才可以在 PHP 端收到的 name 併在一起

nodejs 和 golang 都只要是 name 一樣就可以了

針對這三種語言都有寫了 demo code

只要三種語言的環境裝好

都可以直接起 local server 來試驗看看

html_form_multiple_name

Read more