สุดยอดการจัดการการเก็บรวบรวมข้อมูลในหน่วยความจำด้วย Supergroup.js
เผยแพร่แล้ว: 2022-03-11การจัดการข้อมูลในหน่วยความจำมักส่งผลให้เกิดโค้ดสปาเก็ตตี้จำนวนมาก การจัดการเองอาจง่ายพอ: การจัดกลุ่ม การรวม การสร้างลำดับชั้น และการคำนวณ แต่เมื่อเขียนโค้ด data munging และผลลัพธ์ถูกส่งไปยังส่วนของแอปพลิเคชันที่พวกเขาต้องการ ความต้องการที่เกี่ยวข้องยังคงเกิดขึ้น อาจจำเป็นต้องมีการแปลงข้อมูลที่คล้ายคลึงกันในส่วนอื่นของแอปพลิเคชัน หรืออาจต้องการรายละเอียดเพิ่มเติม: ข้อมูลเมตา บริบท ข้อมูลหลักหรือรอง ฯลฯ ในการแสดงข้อมูลเป็นภาพหรือแอปพลิเคชันการรายงานที่ซับซ้อน โดยเฉพาะอย่างยิ่ง หลังจากที่ใส่ข้อมูลลงในโครงสร้างบางอย่างสำหรับ เมื่อมีความต้องการ เราตระหนักดีว่าคำแนะนำเครื่องมือหรือไฮไลท์ที่ซิงโครไนซ์หรือการเจาะลึกทำให้เกิดแรงกดดันต่อข้อมูลที่แปลงแล้วโดยไม่คาดคิด หนึ่งอาจระบุข้อกำหนดเหล่านี้โดย:
- การบรรจุรายละเอียดเพิ่มเติมและระดับเพิ่มเติมลงในข้อมูลที่แปลงแล้ว จนกว่าจะมีขนาดใหญ่และไม่สมส่วน แต่ตอบสนองความต้องการของทุกซอกทุกมุมของแอปพลิเคชันที่เข้าชมในที่สุด
- การเขียนฟังก์ชันการแปลงรูปแบบใหม่ที่ต้องรวมโหนดที่ประมวลผลแล้วบางส่วนเข้ากับแหล่งข้อมูลส่วนกลางเพื่อนำเสนอรายละเอียดใหม่
- การออกแบบคลาสอ็อบเจ็กต์ที่ซับซ้อนที่รู้วิธีจัดการกับคอนเท็กซ์ทั้งหมดที่อยู่ในนั้น
หลังจากสร้างซอฟต์แวร์ที่เน้นข้อมูลเป็นศูนย์กลางมาเป็นเวลา 20 หรือ 30 ปีเหมือนอย่างฉัน มีคนเริ่มสงสัยว่าพวกเขากำลังแก้ปัญหาชุดเดียวกันซ้ำแล้วซ้ำเล่า เรานำมาซึ่งการวนซ้ำที่ซับซ้อน ความเข้าใจในรายการ ฟังก์ชันการวิเคราะห์ฐานข้อมูล ฟังก์ชัน map หรือ groupBy หรือแม้แต่เครื่องมือการรายงานที่ครบถ้วน ในขณะที่ทักษะของเราพัฒนาขึ้น เราก็สามารถพัฒนาโค้ดการดัดแปลงข้อมูลให้ฉลาดและรัดกุมได้ดีขึ้น แต่ดูเหมือนว่าสปาเก็ตตี้จะยังเพิ่มจำนวนขึ้นเรื่อยๆ
ในบทความนี้ เราจะมาดูที่ไลบรารี JavaScript Supergroup.js ซึ่งมาพร้อมกับฟังก์ชันการจัดการ การจัดกลุ่ม และการรวมข้อมูลในหน่วยความจำที่มีประสิทธิภาพ และจะช่วยให้คุณแก้ปัญหาในการจัดการชุดข้อมูลที่จำกัดได้
ปัญหา
ระหว่างการมีส่วนร่วมกับ Toptal ครั้งแรกของฉัน ฉันมั่นใจตั้งแต่วันแรกว่า API และรูทีนการจัดการข้อมูลของ codebase ที่ฉันเพิ่มนั้นได้รับการระบุมากเกินไปอย่างสิ้นหวัง เป็นแอปพลิเคชัน D3.js สำหรับวิเคราะห์ข้อมูลการตลาด แอปพลิเคชันมีการแสดงภาพแผนภูมิแท่งแบบแบ่งกลุ่ม/ซ้อนกันที่น่าสนใจอยู่แล้ว และจำเป็นต้องมีการสร้างภาพแผนที่ choropleth แผนภูมิแท่งอนุญาตให้ผู้ใช้แสดง 2, 3 หรือ 4 มิติภายในที่เรียกว่า x0, x1, y0 และ y1 โดยที่ x1 และ y1 เป็นตัวเลือก
ในการสร้างตำนาน ตัวกรอง คำแนะนำเครื่องมือ ชื่อ และการคำนวณผลรวมหรือผลต่างปีต่อปี x0, x1, y0 และ y1 ถูกอ้างถึงตลอดทั้งโค้ด และทั่วทั้งโค้ดมีตรรกะตามเงื่อนไขที่ต้องจัดการ การมีหรือไม่มีมิติข้อมูลเสริม
มันอาจจะแย่กว่านี้ก็ได้ โค้ดอาจอ้างอิงโดยตรงไปยังมิติข้อมูลพื้นฐานที่เฉพาะเจาะจง (เช่น ปี งบประมาณ ระดับ ประเภทผลิตภัณฑ์ ฯลฯ) แต่อย่างน้อยก็ทำให้เป็นภาพรวมของมิติที่แสดงของแผนภูมิแท่งแบบจัดกลุ่ม/แบบเรียงซ้อนเป็นอย่างน้อย แต่เมื่อแผนภูมิประเภทอื่นกลายเป็นข้อกำหนด ซึ่งขนาด x0, x1, y0 และ y1 ไม่สมเหตุสมผล ส่วนสำคัญของโค้ดต้องถูกเขียนใหม่ทั้งหมด - โค้ดที่เกี่ยวข้องกับคำอธิบายแผนภูมิ ตัวกรอง คำแนะนำเครื่องมือ ชื่อ การคำนวณสรุป และการสร้างและการเรนเดอร์แผนภูมิ
ไม่มีใครอยากบอกลูกค้าว่า “ฉันรู้ว่านี่เพิ่งจะวันแรกของฉันที่นี่ แต่ก่อนที่ฉันจะใช้สิ่งที่คุณขอ ฉันสามารถจัดโครงสร้างโค้ดทั้งหมดใหม่โดยใช้ไลบรารีจัดการข้อมูล Javascript ที่ฉันเขียนเองได้หรือไม่” ด้วยความโชคดี ฉันรอดพ้นจากความอับอายนี้เมื่อได้รู้จักกับโปรแกรมเมอร์ที่เป็นลูกค้าซึ่งกำลังจะปรับโครงสร้างโค้ดใหม่อยู่แล้ว ด้วยความใจกว้างและความสง่างามที่ไม่ธรรมดา ลูกค้าจึงเชิญฉันเข้าสู่กระบวนการปรับโครงสร้างใหม่ผ่านชุดของเซสชันการเขียนโปรแกรมคู่ เขาเต็มใจที่จะลองใช้ Supergroup.js และภายในไม่กี่นาที เราก็เริ่มแทนที่โค้ดจำนวนมากด้วยการเรียก Supergroup เพียงเล็กน้อย
สิ่งที่เราเห็นในโค้ดนั้นเป็นเรื่องปกติของความยุ่งเหยิงที่เกิดขึ้นในการจัดการกับโครงสร้างข้อมูลแบบลำดับชั้นหรือแบบกลุ่ม โดยเฉพาะอย่างยิ่งในแอปพลิเคชัน D3 เมื่อพวกเขามีขนาดใหญ่กว่าการสาธิต ปัญหาเหล่านี้เกิดขึ้นกับแอปพลิเคชันการรายงานโดยทั่วไป ในแอปพลิเคชัน CRUD ที่เกี่ยวข้องกับการกรองหรือเจาะลึกไปยังหน้าจอหรือบันทึกเฉพาะ ในเครื่องมือวิเคราะห์ เครื่องมือแสดงภาพ แอปพลิเคชันใดๆ ก็ตามที่ใช้ข้อมูลเพียงพอเพื่อต้องการฐานข้อมูล
การจัดการในหน่วยความจำ
ใช้ Rest API สำหรับการค้นหาแบบเหลี่ยมเพชรพลอยและการดำเนินการ CRUD ตัวอย่างเช่น คุณอาจจบลงด้วยการเรียก API หนึ่งรายการขึ้นไปเพื่อรับชุดของฟิลด์และค่า (อาจมีการนับบันทึก) สำหรับพารามิเตอร์การค้นหาทั้งหมด การเรียก API อื่นเพื่อรับ บันทึกเฉพาะ และการโทรอื่นๆ เพื่อรับกลุ่มระเบียนสำหรับการรายงานหรือบางอย่าง ทั้งหมดนี้มีแนวโน้มที่จะซับซ้อนโดยความต้องการกำหนดตัวกรองชั่วคราวตามการเลือกหรือสิทธิ์ของผู้ใช้
หากฐานข้อมูลของคุณไม่น่าจะเกินหมื่นหรือหลายแสนระเบียน หรือหากคุณมีวิธีง่ายๆ ในการจำกัดจักรวาลที่น่าสนใจให้อยู่ในชุดข้อมูลขนาดนั้น คุณอาจทิ้ง Rest API ที่ซับซ้อนทั้งหมด (ยกเว้นส่วนสิทธิ์อนุญาต ) และโทรเพียงครั้งเดียวที่ระบุว่า "ขอข้อมูลทั้งหมดให้ฉัน" เรากำลังอยู่ในโลกที่มีการบีบอัดข้อมูลที่รวดเร็ว ความเร็วในการถ่ายโอนที่รวดเร็ว หน่วยความจำมากมายที่ส่วนหน้า และกลไก Javascript ที่รวดเร็ว การสร้างรูปแบบการสืบค้นที่ซับซ้อนซึ่งจำเป็นต้องเข้าใจและดูแลโดยไคลเอนต์และเซิร์ฟเวอร์มักจะไม่จำเป็น ผู้คนได้เขียนไลบรารีเพื่อเรียกใช้การสืบค้น SQL โดยตรงบนคอลเลกชันของระเบียน JSON เพราะโดยส่วนใหญ่แล้วคุณไม่จำเป็นต้องปรับ RDBMS ให้เหมาะสมทั้งหมด แต่ถึงแม้จะเกินความสามารถ ด้วยความเสี่ยงที่จะฟังดูยิ่งใหญ่ Supergroup นั้นใช้งานง่ายกว่าและมีประสิทธิภาพมากกว่า SQL เกือบทุกครั้ง
Supergroup นั้นเป็น d3.nest, underscore.groupBy หรือ underscore.nest บนเตียรอยด์ ภายใต้ประทุนจะใช้ groupBy ของ lodash สำหรับการดำเนินการจัดกลุ่ม กลยุทธ์หลักคือการทำให้ข้อมูลต้นฉบับทุกชิ้นเป็นข้อมูลเมตา และลิงก์ไปยังส่วนที่เหลือของทรีสามารถเข้าถึงได้ทันทีที่ทุกโหนด และทุกโหนดหรือรายการโหนดนั้นเต็มไปด้วยเค้กแต่งงานที่มีน้ำตาลซินแทคติก ดังนั้นทุกสิ่งส่วนใหญ่ที่คุณอยากรู้จากที่ใดก็ได้บนต้นไม้จะแสดงเป็นนิพจน์สั้นๆ
Supergroup in Action
เพื่อแสดงให้เห็นถึงความหวานทางวากยสัมพันธ์ของ Supergroup ฉันได้ไฮแจ็คสำเนา Mister Nester ของ Shan Carter การซ้อนสองระดับอย่างง่ายโดยใช้ d3.nest ดูเหมือนว่า:
d3.nest() .key(function(d) { return d.year; }) .key(function(d) { return d.fips; }) .map(data);
เทียบเท่ากับ Supergroup จะเป็น:
_.supergroup(data,['year','fips']).d3NestMap();
การเรียกต่อท้ายที่นั่นไปยัง d3NestMap() ทำให้เอาต์พุต Supergroup อยู่ในรูปแบบเดียวกัน (แต่ไม่มีประโยชน์มากในความคิดของฉัน) เป็น nest.map():
{ "1970": { "6001": [ { "fips": "6001", "totalpop": "1073180", "pctHispanic": "0.126", "year": "1970" } ], "6003": [ { "fips": "6003", "totalpop": "510", "pctHispanic": "NA", "year": "1970" } ], ... } }
ฉันพูดว่า "ไม่ค่อยมีประโยชน์" เพราะการเลือก D3 จำเป็นต้องเชื่อมโยงกับอาร์เรย์ ไม่ใช่แผนที่ “โหนด” ในโครงสร้างข้อมูลแผนที่นี้คืออะไร “ 1970” หรือ “6001” เป็นเพียงสตริงและคีย์ในแผนที่ระดับบนสุดหรืออันดับสอง ดังนั้น โหนดจะเป็นสิ่งที่คีย์ชี้ไป “ 1970” ชี้ไปที่แผนที่ระดับที่สอง “6001” ชี้ไปที่อาร์เรย์ของเรคคอร์ดดิบ การซ้อนแผนที่นี้สามารถอ่านได้ในคอนโซล และใช้ได้สำหรับการค้นหาค่า แต่สำหรับการเรียก D3 คุณต้องใช้ข้อมูลอาร์เรย์ ดังนั้นคุณใช้ nest.entries() แทน nest.map():
[ { "key": "1970", "values": [ { "key": "6001", "values": [ { "fips": "6001", "totalpop": "1073180", "pctHispanic": "0.126", "year": "1970" } ] }, { "key": "6003", "values": [ { "fips": "6003", "totalpop": "510", "pctHispanic": "NA", "year": "1970" } ] }, ... ] }, ... ]
ตอนนี้ เรามีอาร์เรย์ของคู่คีย์/ค่าที่ซ้อนกัน: โหนด 1970 มีคีย์เป็น "1970" และค่าที่ประกอบด้วยอาร์เรย์ของคู่คีย์/ค่าระดับที่สอง 6001 เป็นคู่คีย์/ค่าอื่น กุญแจของมันคือสตริงที่ระบุมันด้วย แต่ค่าคืออาร์เรย์ของเร็กคอร์ดดิบ เราต้องปฏิบัติต่อโหนดระดับที่สองถึงระดับใบไม้เหล่านี้ เช่นเดียวกับโหนดระดับใบไม้ที่แตกต่างจากโหนดที่สูงกว่าบนต้นไม้ และตัวโหนดเองก็ไม่มีหลักฐานว่า “1970” คือปี และ “6001” เป็นรหัส fips หรือ 1970 เป็นพาเรนต์ของโหนด 6001 นี้โดยเฉพาะ ฉันจะสาธิตวิธีที่ Supergroup แก้ปัญหาเหล่านี้ แต่ก่อนอื่น ให้ดูที่มูลค่าที่ส่งคืนทันทีของการโทร Supergroup ได้อย่างรวดเร็วก่อน เป็นเพียงอาร์เรย์ของ "คีย์" ระดับบนสุด:
_.supergroup(data,['year','fips']); // [ 1970, 1980, 1990, 2000, 2010 ]
“โอเค ดีมาก” คุณพูด “แต่ข้อมูลที่เหลืออยู่ที่ไหน” สตริงหรือตัวเลขในรายการ Supergroup เป็นอ็อบเจ็กต์ String หรือ Number ซึ่งมีคุณสมบัติและเมธอดมากเกินไป สำหรับโหนดที่อยู่เหนือระดับลีฟ จะมีคุณสมบัติลูก (“ลูก” เป็นชื่อเริ่มต้น คุณสามารถเรียกชื่ออย่างอื่นได้) ที่มีรายการโหนดระดับที่สองของ Supergroup อื่น:
_.supergroup(data,['year','fips'])[0].children; // [ 6001, 6003, 6005, 6007, 6009, 6011, ... ]
ฟังก์ชั่นคำแนะนำเครื่องมือที่ใช้งานได้
เพื่อสาธิตคุณสมบัติอื่นๆ และวิธีการทำงานทั้งหมดนี้ เรามาสร้างรายการซ้อนอย่างง่ายโดยใช้ D3 และดูว่าเราสร้างฟังก์ชันคำแนะนำเครื่องมือที่มีประโยชน์ซึ่งสามารถทำงานกับโหนดใดก็ได้ในรายการได้อย่างไร
d3.select('body') .selectAll('div.year') .data(_.supergroup(data,['year','fips'])) .enter() .append('div').attr('class','year') .on('mouseover', tooltip) .selectAll('div.fips') .data(function(d) { return d.children; }) .enter() .append('div').attr('class','fips') .on('mouseover', tooltip); function tooltip(node) { // comments show values for a second-level node var typeOfNode = node.dim; // fips var nodeValue = node.toString(); // 6001 var totalPopulation = node.aggregate(d3.sum, 'totalpop'); // 1073180 var pathToRoot = node.namePath(); // 1970/6001 var fieldPath = node.dimPath(); // year/fips var rawRecordCount = node.records.length; var parentPop = node.parent.aggregate(d3.sum, 'totalpop'); var percentOfGroup = 100 * totalPopulation / parentPop; var percentOfAll = 100 * totalPopulation / node.path()[0].aggregate(d3.sum,'totalPop'); ... };
ฟังก์ชันคำแนะนำเครื่องมือนี้จะใช้ได้กับเกือบทุกโหนดในทุกระดับความลึก เนื่องจากโหนดที่ระดับบนสุดไม่มีพาเรนต์ เราจึงสามารถแก้ไขปัญหานี้ได้:

var byYearFips = _.supergroup(data,['year','fips']); var root = byYearFips.asRootVal();
ตอนนี้เรามีโหนดรูทที่เป็นพาเรนต์ของโหนดปีทั้งหมด เราไม่ต้องทำอะไรกับมัน แต่ตอนนี้คำแนะนำเครื่องมือของเราจะใช้งานได้เพราะ node.parent มีสิ่งที่จะชี้ให้เห็น และ node.path()[0] ซึ่งควรจะชี้ไปที่โหนดที่แสดงถึงชุดข้อมูลทั้งหมดนั้นจริงๆ แล้ว
ในกรณีที่ไม่ชัดเจนจากตัวอย่างด้านบน namePath, dimPath และ path จะให้เส้นทางจากรูทไปยังโหนดปัจจุบัน:
var byYearFips = _.supergroup(data,['year','fips']); // BTW, you can give a delimiter string to namePath or dimPath otherwise it defaults to '/': byYearFips[0].children[0].namePath(' --> '); // ==> "1970 --> 6001" byYearFips[0].children[0].dimPath(); // ==> "year/fips" byYearFips[0].children[0].path(); // ==> [1970,6001] // after calling asRootVal, paths go up one more level: var root = byYearFips.asRootVal('Population by Year/Fips'); // you can give the root node a name or it defaults to 'Root' byYearFips[0].children[0].namePath(' --> '); // ==> undefined byYearFips[0].children[0].dimPath(); // ==> "root/year/fips" byYearFips[0].children[0].path(); // ==> ["Population by Year/Fips",1970,6001] // from any node, .path()[0] will point to the root: byYearFips[0].children[0].path()[0] === root; // ==> true
รวมเข้าที่เมื่อคุณต้องการ
โค้ดคำแนะนำเครื่องมือด้านบนยังใช้วิธี "รวม" “รวม” ถูกเรียกบนโหนดเดียวและต้องใช้สองพารามิเตอร์:
- ฟังก์ชันการรวมที่ต้องการอาร์เรย์ (โดยปกติคือตัวเลข)
- อาจเป็นชื่อฟิลด์ของฟิลด์ที่จะดึงออกจากเร็กคอร์ดที่จัดกลุ่มภายใต้โหนดนั้นหรือฟังก์ชันที่จะนำไปใช้กับแต่ละเร็กคอร์ดเหล่านั้น
นอกจากนี้ยังมีวิธีอำนวยความสะดวก "รวม" ในรายการ (รายการระดับบนสุดของกลุ่ม หรือกลุ่มย่อยของโหนดใดๆ) มันสามารถกลับรายการหรือแผนที่
_.supergroup(data,'year').aggregates(d3.sum,'totalpop'); // ==> [19957304,23667902,29760021,33871648,37253956] _.supergroup(data,'year').aggregates(d3.sum,'totalpop','dict'); // ==> {"1970":19957304,"1980":23667902,"1990":29760021,"2000":33871648,"2010":37253956}
อาร์เรย์ที่ทำหน้าที่เหมือนแผนที่
ด้วย d3.nest เรามักจะใช้ .entries() มากกว่า .map() อย่างที่ฉันพูดไปก่อนหน้านี้ เพราะ "maps" ไม่อนุญาตให้คุณใช้ฟังก์ชัน D3 (หรือขีดล่าง) ทั้งหมดที่ขึ้นอยู่กับอาร์เรย์ แต่เมื่อคุณใช้ .entries() เพื่อสร้างอาร์เรย์ คุณจะไม่สามารถค้นหาอย่างง่ายด้วยค่าคีย์ได้ แน่นอน Supergroup จัดเตรียมน้ำตาลประโยคที่คุณต้องการ ดังนั้นคุณจึงไม่ต้องเดินผ่านอาร์เรย์ทั้งหมดทุกครั้งที่คุณต้องการค่าเดียว:
_.supergroup(data,['year','fips']).lookup(1980); // ==> 1980 _.supergroup(data,['year','fips']).lookup([1980,6011]).namePath(); // ==> "1980/6011"
การเปรียบเทียบโหนดข้ามเวลา
เมธอด .previous() บนโหนดช่วยให้คุณเข้าถึงโหนดก่อนหน้าในรายการ Supergroup คุณสามารถใช้ .sort(
_.chain(data) .supergroup(['fips','year']) .map(function(fips) { return [fips, _.chain(fips.children.slice(1)) .map(function(year) { return [year, year.aggregate(d3.sum,'totalpop') + ' (' + Math.round( (year.aggregate(d3.sum, 'totalpop') / year.previous().aggregate(d3.sum,'totalpop') - 1) * 100) + '% change from ' + year.previous() + ')' ]; }).object().value() ] }).object().value(); ==> { "6001": { "1980": "1105379 (3% change from 1970)", "1990": "1279182 (16% change from 1980)", "2000": "1443741 (13% change from 1990)", "2010": "1510271 (5% change from 2000)" }, "6003": { "1980": "1097 (115% change from 1970)", "1990": "1113 (1% change from 1980)", "2000": "1208 (9% change from 1990)", "2010": "1175 (-3% change from 2000)" }, ... }
ข้อมูลแบบตารางไปยังเค้าโครงลำดับชั้นของ D3.js
Supergroup ทำได้ดีกว่าที่ฉันได้แสดงไว้ที่นี่ สำหรับการแสดงภาพ D3 ตาม d3.layout.hierarchy โค้ดตัวอย่างในแกลเลอรี D3 โดยทั่วไปจะเริ่มต้นด้วยข้อมูลในรูปแบบทรี (ตัวอย่าง ตัวอย่างแผนผังทรีนี้) Supergroup ช่วยให้คุณเตรียมข้อมูลแบบตารางได้อย่างง่ายดายสำหรับการสร้างภาพข้อมูล d3.layout.hierarchy (ตัวอย่าง) สิ่งที่คุณต้องมีคือโหนดรูทที่ส่งคืนโดย .asRootVal() จากนั้นจึงเรียกใช้ root.addRecordsAsChildrenToLeafNodes() d3.layout.hierarchy คาดว่าโหนดย่อยระดับล่างจะเป็นอาร์เรย์ของเร็กคอร์ดดิบ addRecordsAsChildrenToLeafNodes ใช้โหนดปลายสุดของทรี Supergroup และคัดลอกอาร์เรย์ .records ไปยังคุณสมบัติ .children ไม่ใช่วิธีที่ Supergroup มักจะชอบสิ่งต่างๆ แต่จะทำงานได้ดีสำหรับ Treemaps, Clusters, Partitions ฯลฯ (d3.layout.hierarchy docs)
เช่นเดียวกับเมธอด d3.layout.hierarchy.nodes ที่ส่งคืนโหนดทั้งหมดในทรีเป็นอาร์เรย์เดียว Supergroup จัดเตรียม .descendants() เพื่อรับโหนดทั้งหมดโดยเริ่มจากโหนดเฉพาะ .flattenTree() เพื่อให้โหนดทั้งหมดเริ่มต้น จากรายการ Supergroup ปกติ และ .leafNodes() เพื่อรับเพียงอาร์เรย์ของโหนดลีฟ
การจัดกลุ่มและการรวมกลุ่มตามเขตข้อมูลที่มีหลายค่า
โดยไม่ต้องลงรายละเอียดให้ละเอียดถี่ถ้วน ฉันจะพูดถึงว่า Supergroup มีคุณสมบัติบางอย่างสำหรับจัดการกับสถานการณ์ที่เกิดขึ้นไม่บ่อยนัก แต่โดยทั่วไปเพียงพอที่จะได้รับการดูแลเป็นพิเศษ
บางครั้งคุณต้องการจัดกลุ่มตามเขตข้อมูลที่สามารถมีค่าได้มากกว่าหนึ่งค่า ในเชิงสัมพันธ์หรือแบบตาราง โดยทั่วไป ฟิลด์ที่มีหลายค่าไม่ควรเกิดขึ้น (ซึ่งทำลายรูปแบบปกติอันดับแรก) แต่อาจมีประโยชน์ นี่คือวิธีที่ Supergroup จัดการกับกรณีดังกล่าว:
var bloggers = [ { name:"Ridwan", profession:["Programmer"], articlesPublished:73 }, { name:"Sigfried", profession:["Programmer","Spiritualist"], articlesPublished:2 }, ]; // the regular way _.supergroup(bloggers, 'profession').aggregates(_.sum, 'articlesPublished','dict'); // ==> {"Programmer":73,"Programmer,Spiritualist":2} // with multiValuedGroups _.supergroup(bloggers, 'profession',{multiValuedGroups:true}).aggregates(_.sum, 'articlesPublished','dict'); // ==> {"Programmer":75,"Spiritualist":2}
อย่างที่คุณเห็น ด้วย multiValuedGroup ผลรวมของบทความทั้งหมดที่เผยแพร่ในรายการกลุ่มจะสูงกว่าจำนวนบทความทั้งหมดจริงที่เผยแพร่เนื่องจากระเบียน Sigfried ถูกนับสองครั้ง บางครั้งนี่เป็นพฤติกรรมที่ต้องการ
เปลี่ยนตารางลำดับชั้นเป็นต้นไม้
อีกสิ่งหนึ่งที่สามารถเกิดขึ้นได้เป็นครั้งคราวคือโครงสร้างแบบตารางที่แสดงถึงทรีผ่านความสัมพันธ์แบบพาเรนต์/รองที่ชัดเจนระหว่างเร็กคอร์ด นี่คือตัวอย่างอนุกรมวิธานขนาดเล็ก:
พี | ค |
---|---|
สัตว์ | สัตว์เลี้ยงลูกด้วยนม |
สัตว์ | สัตว์เลื้อยคลาน |
สัตว์ | ปลา |
สัตว์ | นก |
ปลูก | ต้นไม้ |
ปลูก | หญ้า |
ต้นไม้ | ต้นโอ๊ก |
ต้นไม้ | เมเปิ้ล |
ต้นโอ๊ก | พินโอ๊ค |
สัตว์เลี้ยงลูกด้วยนม | เจ้าคณะ |
สัตว์เลี้ยงลูกด้วยนม | วัว |
วัว | วัว |
วัว | วัว |
เจ้าคณะ | ลิง |
เจ้าคณะ | ลิง |
ลิง | ชิมแปนซี |
ลิง | กอริลลา |
ลิง | ฉัน |
tree = _.hierarchicalTableToTree(taxonomy, 'p', 'c'); // top-level nodes ==> ["animal","plant"] _.invoke(tree.flattenTree(), 'namePath'); // call namePath on every node ==> ["animal", "animal/mammal", "animal/mammal/primate", "animal/mammal/primate/monkey", "animal/mammal/primate/ape", "animal/mammal/primate/ape/chimpanzee", "animal/mammal/primate/ape/gorilla", "animal/mammal/primate/ape/me", "animal/mammal/bovine", "animal/mammal/bovine/cow", "animal/mammal/bovine/ox", "animal/reptile", "animal/fish", "animal/bird", "plant", "plant/tree", "plant/tree/oak", "plant/tree/oak/pin oak", "plant/tree/maple", "plant/grass"]
บทสรุป
ดังนั้นเราจึงมีมัน ฉันใช้ Supergroup กับทุกโปรเจ็กต์ Javascript ที่ฉันเคยทำงานมาในช่วงสามปีที่ผ่านมา ฉันรู้ว่ามันแก้ปัญหามากมายที่เกิดขึ้นอย่างต่อเนื่องในการเขียนโปรแกรมที่เน้นข้อมูลเป็นศูนย์กลาง API และการใช้งานไม่ได้สมบูรณ์แบบเลย และฉันยินดีที่จะหาผู้ทำงานร่วมกันที่สนใจร่วมงานกับฉัน
หลังจากสองสามวันของการปรับโครงสร้างโปรเจ็กต์ไคลเอนต์นั้น ฉันได้รับข้อความจาก Dave โปรแกรมเมอร์ที่ฉันทำงานด้วย:
เดฟ: ฉันต้องบอกว่าฉันเป็นแฟนตัวยงของซูเปอร์กรุ๊ป มันทำความสะอาดตัน
ซิกฟรีด: เย้ ฉันจะขอคำรับรองในบางประเด็น :)
เดฟ: ฮ่าอย่างแน่นอน
หากคุณลองใช้งานและมีคำถามหรือปัญหาใดๆ เกิดขึ้น ให้วางบรรทัดในส่วนความคิดเห็นหรือโพสต์ปัญหาในที่เก็บ GitHub