วิธีที่มีประสิทธิภาพมากที่สุดในการจัดกลุ่มตามอาร์เรย์ของวัตถุ


507

วิธีที่มีประสิทธิภาพมากที่สุดในการจัดกลุ่มวัตถุในอาร์เรย์คืออะไร?

ตัวอย่างเช่นกำหนดอาร์เรย์ของวัตถุนี้:

[ 
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
]

ฉันกำลังแสดงข้อมูลนี้ในตาราง ฉันต้องการจัดกลุ่มตามวิธีการที่แตกต่างกัน แต่ฉันต้องการรวมค่า

ฉันใช้ Underscore.js สำหรับฟังก์ชั่น groupby ซึ่งมีประโยชน์ แต่ไม่ได้ทำเคล็ดลับทั้งหมดเพราะฉันไม่ต้องการให้พวกเขา "แยก" แต่ "ผสาน" เหมือนgroup byวิธีSQL

สิ่งที่ฉันกำลังมองหาจะสามารถรวมค่าเฉพาะ (ถ้ามีการร้องขอ)

ดังนั้นถ้าฉันเป็นกลุ่มPhaseโดยฉันต้องการรับ:

[
    { Phase: "Phase 1", Value: 50 },
    { Phase: "Phase 2", Value: 130 }
]

และถ้าฉันทำ groupy Phase/ Stepฉันจะได้รับ:

[
    { Phase: "Phase 1", Step: "Step 1", Value: 15 },
    { Phase: "Phase 1", Step: "Step 2", Value: 35 },
    { Phase: "Phase 2", Step: "Step 1", Value: 55 },
    { Phase: "Phase 2", Step: "Step 2", Value: 75 }
]

มีสคริปต์ที่เป็นประโยชน์สำหรับสิ่งนี้หรือฉันควรใช้ Underscore.js แล้ววนลูปผ่านวัตถุที่เป็นผลลัพธ์เพื่อทำยอดรวมเอง

คำตอบ:


755

หากคุณต้องการหลีกเลี่ยงไลบรารีภายนอกคุณสามารถใช้เวอร์ชันของวานิลลาได้อย่างกระชับgroupBy()ดังนี้:

var groupBy = function(xs, key) {
  return xs.reduce(function(rv, x) {
    (rv[x[key]] = rv[x[key]] || []).push(x);
    return rv;
  }, {});
};

console.log(groupBy(['one', 'two', 'three'], 'length'));

// => {3: ["one", "two"], 5: ["three"]}


18
ฉันจะแก้ไขด้วยวิธีนี้: `` `return xs.reduce (ฟังก์ชั่น (rv, x) {var v = อินสแตนซ์ของฟังก์ชัน key? key (x): x [key]; (rv [v] = rv [v] || []). push (x); return rv;}, {}); `` `อนุญาตให้ฟังก์ชันการเรียกกลับสามารถส่งคืนเกณฑ์การเรียงลำดับ
y_nk

110
นี่คือสิ่งที่อาร์เรย์เอาต์พุตและไม่ใช่วัตถุ: groupByArray (xs, key) {return xs.reduce (ฟังก์ชั่น (rv, x) {let v = อินสแตนซ์ keyof ของ Function? key (x): x [กุญแจ]; .find ((r) => r && r.key === v); ถ้า (el) {el.values.push (x);} อื่น {rv.push ({key: v, ค่า: [x] });} return rv;}, []); }
tomitrescak

24
เยี่ยมมากสิ่งที่ฉันต้องการ ในกรณีที่คนอื่นต้องการมันนี่คือลายเซ็น TypeScript:var groupBy = function<TItem>(xs: TItem[], key: string) : {[key: string]: TItem[]} { ...
Michael Sandino

4
สำหรับสิ่งที่คุ้มค่าโซลูชันของ tomitrescak ในขณะที่สะดวกมีประสิทธิภาพน้อยกว่าอย่างเห็นได้ชัดเนื่องจาก find () น่าจะเป็น O (n) วิธีแก้ปัญหาในคำตอบคือ O (n) จากการลด (การมอบหมายวัตถุคือ O (1) ตามที่กด) ในขณะที่ความคิดเห็นคือ O (n) * O (n) หรือ O (n ^ 2) หรือที่ อย่างน้อย O (nlgn)
narthur157

21
หากใครสนใจฉันได้สร้างฟังก์ชั่นนี้ให้อ่านง่ายขึ้นและใส่คำอธิบายประกอบไว้และสรุปเป็นส่วนสำคัญ: gist.github.com/robmathers/1830ce09695f759bf2c4df15c29dd22dฉันคิดว่ามันมีประโยชน์สำหรับการทำความเข้าใจสิ่งที่เกิดขึ้นจริงที่นี่
robmathers

228

ใช้วัตถุแผนที่ ES6:

function groupBy(list, keyGetter) {
    const map = new Map();
    list.forEach((item) => {
         const key = keyGetter(item);
         const collection = map.get(key);
         if (!collection) {
             map.set(key, [item]);
         } else {
             collection.push(item);
         }
    });
    return map;
}

// example usage

const pets = [
    {type:"Dog", name:"Spot"},
    {type:"Cat", name:"Tiger"},
    {type:"Dog", name:"Rover"}, 
    {type:"Cat", name:"Leo"}
];
    
const grouped = groupBy(pets, pet => pet.type);
    
console.log(grouped.get("Dog")); // -> [{type:"Dog", name:"Spot"}, {type:"Dog", name:"Rover"}]
console.log(grouped.get("Cat")); // -> [{type:"Cat", name:"Tiger"}, {type:"Cat", name:"Leo"}]
    
    

เกี่ยวกับแผนที่: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map


@ortb วิธีการรับโดยไม่ต้องเรียกget()วิธีการหรือไม่ ซึ่งฉันต้องการแสดงผลโดยไม่ต้องผ่านคีย์
Fai Zal Dong

@FaiZalDong: ฉันไม่แน่ใจว่าสิ่งที่จะดีที่สุดสำหรับกรณีของคุณ? ถ้าฉันเขียนconsole.log(grouped.entries());ในตัวอย่าง jsfiddle มันจะส่งคืน iterable ที่ทำงานเหมือนอาร์เรย์ของคีย์ + ค่า คุณลองและดูว่ามันช่วยได้ไหม
mortb

7
คุณสามารถลองconsole.log(Array.from(grouped));
mortb

ฉันรักคำตอบนี้ยืดหยุ่นมาก
benshabatnoam

เพื่อดูจำนวนองค์ประกอบในกลุ่ม:Array.from(groupBy(jsonObj, item => i.type)).map(i => ( {[i[0]]: i[1].length} ))
Ahmet Şimşek

105

ด้วย ES6:

const groupBy = (items, key) => items.reduce(
  (result, item) => ({
    ...result,
    [item[key]]: [
      ...(result[item[key]] || []),
      item,
    ],
  }), 
  {},
);

3
มันใช้เวลาเล็กน้อยในการทำความคุ้นเคย แต่ทำส่วนใหญ่ของ C ++ เทมเพลตด้วยเช่นกัน
Levi Haskell

2
...resultฉันทรมานสมองของฉันและยังคงไม่เข้าใจว่าในโลกการทำงานเริ่มต้นจาก ตอนนี้ฉันนอนไม่หลับเพราะสิ่งนั้น

8
สง่า แต่เจ็บปวดช้าในอาร์เรย์ขนาดใหญ่!
infinity1975

4
lol นี่คืออะไร
Nino Škopac

1
ทางออกที่ง่าย ขอบคุณ
Ezequiel Tavares

59

แม้ว่าคำตอบของlinqนั้นน่าสนใจ แต่ก็ค่อนข้างหนัก แนวทางของฉันค่อนข้างแตกต่าง:

var DataGrouper = (function() {
    var has = function(obj, target) {
        return _.any(obj, function(value) {
            return _.isEqual(value, target);
        });
    };

    var keys = function(data, names) {
        return _.reduce(data, function(memo, item) {
            var key = _.pick(item, names);
            if (!has(memo, key)) {
                memo.push(key);
            }
            return memo;
        }, []);
    };

    var group = function(data, names) {
        var stems = keys(data, names);
        return _.map(stems, function(stem) {
            return {
                key: stem,
                vals:_.map(_.where(data, stem), function(item) {
                    return _.omit(item, names);
                })
            };
        });
    };

    group.register = function(name, converter) {
        return group[name] = function(data, names) {
            return _.map(group(data, names), converter);
        };
    };

    return group;
}());

DataGrouper.register("sum", function(item) {
    return _.extend({}, item.key, {Value: _.reduce(item.vals, function(memo, node) {
        return memo + Number(node.Value);
    }, 0)});
});

คุณสามารถดูได้ในการกระทำที่ JSBin

ฉันไม่เห็นสิ่งใดในขีดล่างที่ทำในสิ่งที่hasทำถึงแม้ว่าฉันอาจหายไป มันเหมือนกัน_.containsมาก แต่ใช้_.isEqualแทน===การเปรียบเทียบ นอกเหนือจากนั้นส่วนที่เหลือเป็นเฉพาะปัญหาถึงแม้จะมีความพยายามทั่วไปก็ตาม

ตอนนี้DataGrouper.sum(data, ["Phase"])กลับมา

[
    {Phase: "Phase 1", Value: 50},
    {Phase: "Phase 2", Value: 130}
]

และDataGrouper.sum(data, ["Phase", "Step"])ผลตอบแทน

[
    {Phase: "Phase 1", Step: "Step 1", Value: 15},
    {Phase: "Phase 1", Step: "Step 2", Value: 35},
    {Phase: "Phase 2", Step: "Step 1", Value: 55},
    {Phase: "Phase 2", Step: "Step 2", Value: 75}
]

แต่sumนี่เป็นเพียงฟังก์ชันอันหนึ่งที่มีศักยภาพ คุณสามารถลงทะเบียนผู้อื่นตามที่คุณต้องการ:

DataGrouper.register("max", function(item) {
    return _.extend({}, item.key, {Max: _.reduce(item.vals, function(memo, node) {
        return Math.max(memo, Number(node.Value));
    }, Number.NEGATIVE_INFINITY)});
});

และตอนนี้DataGrouper.max(data, ["Phase", "Step"])จะกลับมา

[
    {Phase: "Phase 1", Step: "Step 1", Max: 10},
    {Phase: "Phase 1", Step: "Step 2", Max: 20},
    {Phase: "Phase 2", Step: "Step 1", Max: 30},
    {Phase: "Phase 2", Step: "Step 2", Max: 40}
]

หรือถ้าคุณลงทะเบียนสิ่งนี้:

DataGrouper.register("tasks", function(item) {
    return _.extend({}, item.key, {Tasks: _.map(item.vals, function(item) {
      return item.Task + " (" + item.Value + ")";
    }).join(", ")});
});

จากนั้นโทรDataGrouper.tasks(data, ["Phase", "Step"])จะได้รับคุณ

[
    {Phase: "Phase 1", Step: "Step 1", Tasks: "Task 1 (5), Task 2 (10)"},
    {Phase: "Phase 1", Step: "Step 2", Tasks: "Task 1 (15), Task 2 (20)"},
    {Phase: "Phase 2", Step: "Step 1", Tasks: "Task 1 (25), Task 2 (30)"},
    {Phase: "Phase 2", Step: "Step 2", Tasks: "Task 1 (35), Task 2 (40)"}
]

DataGrouperตัวเองเป็นฟังก์ชั่น คุณสามารถโทรหาด้วยข้อมูลและรายการคุณสมบัติที่คุณต้องการจัดกลุ่มตาม มันจะส่งกลับอาร์เรย์ที่มีองค์ประกอบเป็นวัตถุที่มีสองคุณสมบัติ: keyเป็นชุดของคุณสมบัติที่จัดกลุ่มvalsเป็นอาร์เรย์ของวัตถุที่มีคุณสมบัติที่เหลือไม่ได้อยู่ในคีย์ ตัวอย่างเช่นDataGrouper(data, ["Phase", "Step"])จะให้ผล:

[
    {
        "key": {Phase: "Phase 1", Step: "Step 1"},
        "vals": [
            {Task: "Task 1", Value: "5"},
            {Task: "Task 2", Value: "10"}
        ]
    },
    {
        "key": {Phase: "Phase 1", Step: "Step 2"},
        "vals": [
            {Task: "Task 1", Value: "15"}, 
            {Task: "Task 2", Value: "20"}
        ]
    },
    {
        "key": {Phase: "Phase 2", Step: "Step 1"},
        "vals": [
            {Task: "Task 1", Value: "25"},
            {Task: "Task 2", Value: "30"}
        ]
    },
    {
        "key": {Phase: "Phase 2", Step: "Step 2"},
        "vals": [
            {Task: "Task 1", Value: "35"}, 
            {Task: "Task 2", Value: "40"}
        ]
    }
]

DataGrouper.registerยอมรับฟังก์ชั่นและสร้างฟังก์ชั่นใหม่ซึ่งยอมรับข้อมูลเริ่มต้นและคุณสมบัติในการจัดกลุ่มตาม ฟังก์ชั่นใหม่นี้จะใช้รูปแบบเอาต์พุตตามที่แสดงไว้ด้านบนและเรียกใช้ฟังก์ชันของคุณกับแต่ละฟังก์ชันโดยส่งคืนอาร์เรย์ใหม่ ฟังก์ชั่นที่สร้างขึ้นจะถูกจัดเก็บเป็นคุณสมบัติDataGrouperตามชื่อที่คุณระบุและส่งคืนหากคุณต้องการการอ้างอิงในท้องถิ่น

นั่นเป็นคำอธิบายมากมาย รหัสตรงไปตรงมาสมเหตุสมผลฉันหวังว่า!


สวัสดี .. สามารถเห็นคุณจัดกลุ่มตามและรวมเพียงค่า แต่ในกรณีที่ฉันต้องการผลรวมโดย value1 และ value2 และ value3 ... คุณมีทางออกหรือไม่
SAMUEL OSPINA

@SAMUELOSPINA คุณเคยพบวิธีการทำเช่นนี้หรือไม่?
howMuchCheeseIsTooMuchCheese

50

ฉันจะตรวจสอบกลุ่มบ้านพักโดยดูเหมือนว่าจะทำสิ่งที่คุณกำลังมองหา มันค่อนข้างเบาและเรียบง่ายจริงๆ

ตัวอย่างซอ: https://jsfiddle.net/r7szvt5k/

ระบุว่าชื่ออาร์เรย์ของคุณคือarrgroupBy with lodash เป็นเพียง:

import groupBy from 'lodash/groupBy';
// if you still use require:
// const groupBy = require('lodash/groupBy');

const a = groupBy(arr, function(n) {
  return n.Phase;
});
// a is your array grouped by Phase attribute

1
คำตอบนี้ไม่เป็นปัญหาหรือไม่ มีหลายวิธีที่ Lodash _.group โดยผลลัพธ์ไม่อยู่ในรูปแบบของผลลัพธ์ที่ OP ขอ (1) ผลลัพธ์ไม่ใช่อาร์เรย์ (2) "ค่า" กลายเป็น "กุญแจ" ในผลลัพธ์ของวัตถุ lodash
mg1075

44

สิ่งนี้อาจทำได้ง่ายกว่าด้วยlinq.jsซึ่งมีวัตถุประสงค์เพื่อให้การนำ LINQ ไปใช้จริงใน JavaScript ( DEMO ):

var linq = Enumerable.From(data);
var result =
    linq.GroupBy(function(x){ return x.Phase; })
        .Select(function(x){
          return {
            Phase: x.Key(),
            Value: x.Sum(function(y){ return y.Value|0; })
          };
        }).ToArray();

ผลลัพธ์:

[
    { Phase: "Phase 1", Value: 50 },
    { Phase: "Phase 2", Value: 130 }
]

หรือมากกว่านั้นโดยใช้ตัวเลือกแบบอิงสตริง ( DEMO ):

linq.GroupBy("$.Phase", "",
    "k,e => { Phase:k, Value:e.Sum('$.Value|0') }").ToArray();

เราสามารถใช้พร็อพเพอร์ตี้หลายรายการพร้อมกันได้ที่นี่:GroupBy(function(x){ return x.Phase; })
Amit

38

คุณสามารถสร้าง ES6 จากMaparray.reduce()

const groupedMap = initialArray.reduce(
    (entryMap, e) => entryMap.set(e.id, [...entryMap.get(e.id)||[], e]),
    new Map()
);

นี่เป็นข้อได้เปรียบเหนือโซลูชันอื่น ๆ :

  • มันไม่จำเป็นต้องมีห้องสมุดใด ๆ (ไม่เหมือนเช่น_.groupBy())
  • คุณได้รับ JavaScript Mapแทนวัตถุ (เช่นส่งคืนโดย_.groupBy()) สิ่งนี้มีประโยชน์มากมายรวมไปถึง:
    • มันจะจำคำสั่งในการเพิ่มรายการเป็นครั้งแรก
    • คีย์สามารถเป็นชนิดใดก็ได้แทนที่จะเป็นแค่สตริง
  • A Mapเป็นผลลัพธ์ที่มีประโยชน์มากขึ้นซึ่งอาร์เรย์ของอาร์เรย์ แต่ถ้าคุณต้องการอาร์เรย์ของอาร์เรย์คุณสามารถเรียกArray.from(groupedMap.entries())(สำหรับอาร์เรย์ของ[key, group array]คู่) หรือArray.from(groupedMap.values())(สำหรับอาร์เรย์ที่เรียบง่าย)
  • มันค่อนข้างยืดหยุ่น บ่อยครั้งที่สิ่งที่คุณวางแผนที่จะทำต่อไปกับแผนที่นี้สามารถทำได้โดยตรงซึ่งเป็นส่วนหนึ่งของการลด

เป็นตัวอย่างของจุดสุดท้ายลองจินตนาการว่าฉันมีชุดของวัตถุที่ฉันต้องการผสาน (ตื้น) โดยใช้ id ดังนี้:

const objsToMerge = [{id: 1, name: "Steve"}, {id: 2, name: "Alice"}, {id: 1, age: 20}];
// The following variable should be created automatically
const mergedArray = [{id: 1, name: "Steve", age: 20}, {id: 2, name: "Alice"}]

เมื่อต้องการทำเช่นนี้ฉันมักจะเริ่มต้นด้วยการจัดกลุ่มตามรหัสแล้วผสานแต่ละอาร์เรย์ผลลัพธ์ คุณสามารถทำการผสานโดยตรงในreduce():

const mergedArray = Array.from(
    objsToMerge.reduce(
        (entryMap, e) => entryMap.set(e.id, {...entryMap.get(e.id)||{}, ...e}),
        new Map()
    ).values()
);

1
ฉันไม่รู้ว่าทำไมสิ่งนี้จึงไม่มีคะแนนเพิ่มเติม มันกระชับอ่านง่าย (สำหรับฉัน) และดูมีประสิทธิภาพ มันไม่ได้บินบน IE11แต่ชุดติดตั้งเพิ่มเติมนั้นไม่ยากเกินไป ( a.reduce(function(em, e){em.set(e.id, (em.get(e.id)||[]).concat([e]));return em;}, new Map())ประมาณ)
35425


18

คุณสามารถทำได้ด้วยAlasql JavaScript library:

var data = [ { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
             { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" }];

var res = alasql('SELECT Phase, Step, SUM(CAST([Value] AS INT)) AS [Value] \
                  FROM ? GROUP BY Phase, Step',[data]);

ลองตัวอย่างนี้ที่ jsFiddle

BTW:บนอาร์เรย์ขนาดใหญ่ (บันทึก 100,000 รายการและอื่น ๆ ) Alasql เร็วกว่า Linq ดูการทดสอบที่ jsPref

ความคิดเห็นที่:

  • ที่นี่ฉันใส่ Value ในวงเล็บเหลี่ยมเพราะ VALUE เป็นคำหลักใน SQL
  • ฉันต้องใช้ฟังก์ชัน CAST () เพื่อแปลงค่าสตริงเป็นประเภทตัวเลข

18
Array.prototype.groupBy = function(keyFunction) {
    var groups = {};
    this.forEach(function(el) {
        var key = keyFunction(el);
        if (key in groups == false) {
            groups[key] = [];
        }
        groups[key].push(el);
    });
    return Object.keys(groups).map(function(key) {
        return {
            key: key,
            values: groups[key]
        };
    });
};

15

MDN มีตัวอย่างนี้ในArray.reduce()เอกสารประกอบ

// Grouping objects by a property
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Grouping_objects_by_a_property#Grouping_objects_by_a_property

var people = [
  { name: 'Alice', age: 21 },
  { name: 'Max', age: 20 },
  { name: 'Jane', age: 20 }
];

function groupBy(objectArray, property) {
  return objectArray.reduce(function (acc, obj) {
    var key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}

var groupedPeople = groupBy(people, 'age');
// groupedPeople is:
// { 
//   20: [
//     { name: 'Max', age: 20 }, 
//     { name: 'Jane', age: 20 }
//   ], 
//   21: [{ name: 'Alice', age: 21 }] 
// }

14

แม้ว่าคำถามที่มีคำตอบบางอย่างและคำตอบดูบิตซับซ้อนกว่าผมขอแนะนำให้ใช้วานิลลา Javascript สำหรับกลุ่มโดยมีซ้อนกัน Map(ถ้าจำเป็น)

function groupBy(array, groups, valueKey) {
    var map = new Map;
    groups = [].concat(groups);
    return array.reduce((r, o) => {
        groups.reduce((m, k, i, { length }) => {
            var child;
            if (m.has(o[k])) return m.get(o[k]);
            if (i + 1 === length) {
                child = Object
                    .assign(...groups.map(k => ({ [k]: o[k] })), { [valueKey]: 0 });
                r.push(child);
            } else {
                child = new Map;
            }
            m.set(o[k], child);
            return child;
        }, map)[valueKey] += +o[valueKey];
        return r;
    }, [])
};

var data = [{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" }, { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" }, { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" }, { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" }, { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" }, { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" }, { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" }, { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }];

console.log(groupBy(data, 'Phase', 'Value'));
console.log(groupBy(data, ['Phase', 'Step'], 'Value'));
.as-console-wrapper { max-height: 100% !important; top: 0; }


9

ไม่มีการกลายพันธุ์:

const groupBy = (xs, key) => xs.reduce((acc, x) => Object.assign({}, acc, {
  [x[key]]: (acc[x[key]] || []).concat(x)
}), {})

console.log(groupBy(['one', 'two', 'three'], 'length'));
// => {3: ["one", "two"], 5: ["three"]}

8

โซลูชันนี้ใช้ฟังก์ชันใดก็ได้ (ไม่ใช่คีย์) ดังนั้นจึงมีความยืดหยุ่นมากกว่าโซลูชันด้านบนและอนุญาตให้ใช้ฟังก์ชันลูกศรซึ่งคล้ายกับการแสดงออกแลมบ์ดาที่ใช้ในLINQ :

Array.prototype.groupBy = function (funcProp) {
    return this.reduce(function (acc, val) {
        (acc[funcProp(val)] = acc[funcProp(val)] || []).push(val);
        return acc;
    }, {});
};

หมายเหตุ: ไม่ว่าคุณจะต้องการขยายArrayต้นแบบขึ้นอยู่กับคุณหรือไม่

ตัวอย่างที่รองรับในเบราว์เซอร์ส่วนใหญ่:

[{a:1,b:"b"},{a:1,c:"c"},{a:2,d:"d"}].groupBy(function(c){return c.a;})

ตัวอย่างการใช้ฟังก์ชั่นลูกศร (ES6):

[{a:1,b:"b"},{a:1,c:"c"},{a:2,d:"d"}].groupBy(c=>c.a)

ตัวอย่างทั้งสองข้างต้นกลับมา:

{
  "1": [{"a": 1, "b": "b"}, {"a": 1, "c": "c"}],
  "2": [{"a": 2, "d": "d"}]
}

ฉันชอบโซลูชัน ES6 มาก เพียงเล็กน้อย semplification โดยไม่ขยายต้นแบบ Array:let key = 'myKey'; let newGroupedArray = myArrayOfObjects.reduce(function (acc, val) { (acc[val[key]] = acc[val[key]] || []).push(val); return acc;});
caneta

8

ฉันอยากจะแนะนำวิธีการของฉัน ขั้นแรกให้แยกการจัดกลุ่มและการรวม ให้ประกาศฟังก์ชัน "group by" ต้นแบบ มันใช้ฟังก์ชั่นอื่นในการผลิตสตริง "hash" สำหรับแต่ละองค์ประกอบของอาร์เรย์

Array.prototype.groupBy = function(hash){
  var _hash = hash ? hash : function(o){return o;};

  var _map = {};
  var put = function(map, key, value){
    if (!map[_hash(key)]) {
        map[_hash(key)] = {};
        map[_hash(key)].group = [];
        map[_hash(key)].key = key;

    }
    map[_hash(key)].group.push(value); 
  }

  this.map(function(obj){
    put(_map, obj, obj);
  });

  return Object.keys(_map).map(function(key){
    return {key: _map[key].key, group: _map[key].group};
  });
}

เมื่อจัดกลุ่มเสร็จแล้วคุณสามารถรวบรวมข้อมูลตามที่คุณต้องการในกรณีของคุณ

data.groupBy(function(o){return JSON.stringify({a: o.Phase, b: o.Step});})
    /* aggreagating */
    .map(function(el){ 
         var sum = el.group.reduce(
           function(l,c){
             return l + parseInt(c.Value);
           },
           0
         );
         el.key.Value = sum; 
         return el.key;
    });

มันใช้งานได้เหมือนกัน ฉันทดสอบโค้ดนี้ในคอนโซลโครเมี่ยมแล้ว และรู้สึกฟรีเพื่อปรับปรุงและค้นหาข้อผิดพลาด;)


ขอบคุณมาก! รักวิธีการและเหมาะสมกับความต้องการของฉันอย่างสมบูรณ์แบบ (ฉันไม่ต้องการการรวม)
aberaud

ฉันคิดว่าคุณต้องการที่จะเปลี่ยนสายงานของคุณในใส่ (): การmap[_hash(key)].key = key; map[_hash(key)].key = _hash(key);
Scotty.NET

6

ลองนึกภาพว่าคุณมีสิ่งนี้:

[{id:1, cat:'sedan'},{id:2, cat:'sport'},{id:3, cat:'sport'},{id:4, cat:'sedan'}]

โดยการทำสิ่งนี้: const categories = [...new Set(cars.map((car) => car.cat))]

คุณจะได้รับสิ่งนี้: ['sedan','sport']

คำอธิบาย: 1. ขั้นแรกเราจะสร้างชุดใหม่โดยผ่านอาร์เรย์ เนื่องจาก Set อนุญาตเฉพาะค่าที่ไม่ซ้ำกันรายการที่ซ้ำกันทั้งหมดจะถูกลบ

  1. ตอนนี้สำเนาที่ซ้ำกันหายไปเราจะแปลงกลับเป็นอาร์เรย์โดยใช้ตัวดำเนินการสเปรด ...

ตั้งค่า Doc: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set Spread OperatorDoc: https://developer.mozilla.org/en-US/docs/Web/JavaScript / อ้างอิง / ผู้ประกอบการ / Spread_syntax


ฉันชอบคำตอบของคุณมากมันสั้นที่สุด แต่ฉันก็ยังไม่เข้าใจตรรกะโดยเฉพาะผู้ที่จัดกลุ่มที่นี่ มันแพร่กระจายตัวดำเนินการ (... )? หรือ 'ชุดใหม่ ()' โปรดอธิบายให้เราฟัง ... ขอบคุณ
อีวาน

1
1. อันดับแรกเราจะสร้างชุดใหม่โดยผ่านอาร์เรย์ เนื่องจาก Set อนุญาตเฉพาะค่าที่ไม่ซ้ำกันรายการที่ซ้ำกันทั้งหมดจะถูกลบ 2. ตอนนี้รายการที่ซ้ำกันจะหายไปเราจะแปลงกลับเป็นอาร์เรย์โดยใช้ตัวดำเนินการสเปรด... ตั้งค่า Doc: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ...... Spread ผู้ให้บริการ: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
......

ตกลงเข้าใจแล้ว ... ขอบคุณสำหรับคำอธิบายของคุณ :) :)
อีวาน

ยินดีต้อนรับคุณ!
Yago Gehres

6

ตรวจสอบคำตอบ - เพียงการจัดกลุ่มตื้น มันค่อนข้างดีที่จะเข้าใจการลดลง คำถามยังให้ปัญหาการคำนวณรวมเพิ่มเติม

นี่คือ REAL GROUP BY สำหรับ Array of Object โดยบางฟิลด์ที่มี 1) ชื่อคีย์ที่คำนวณได้และ 2) โซลูชันที่สมบูรณ์สำหรับการเรียงลำดับการจัดกลุ่มโดยให้รายการของคีย์ที่ต้องการและแปลงค่าที่ไม่ซ้ำกันเป็นรูทเช่น SQL GROUP โดยไม่

const inputArray = [ 
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
];

var outObject = inputArray.reduce(function(a, e) {
  // GROUP BY estimated key (estKey), well, may be a just plain key
  // a -- Accumulator result object
  // e -- sequentally checked Element, the Element that is tested just at this itaration

  // new grouping name may be calculated, but must be based on real value of real field
  let estKey = (e['Phase']); 

  (a[estKey] ? a[estKey] : (a[estKey] = null || [])).push(e);
  return a;
}, {});

console.log(outObject);

เล่นกับestKey- คุณสามารถจัดกลุ่มมากกว่าหนึ่งฟิลด์เพิ่มการรวมเพิ่มเติมการคำนวณหรือการประมวลผลอื่น ๆ

นอกจากนี้คุณยังสามารถจัดกลุ่มข้อมูลซ้ำ ตัวอย่างเช่นเริ่มแรกจัดกลุ่มตามPhaseจากนั้นตามStepฟิลด์และอื่น ๆ นอกจากนี้ยังระเบิดข้อมูลที่เหลือไขมัน

const inputArray = [
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
  ];

/**
 * Small helper to get SHALLOW copy of obj WITHOUT prop
 */
const rmProp = (obj, prop) => ( (({[prop]:_, ...rest})=>rest)(obj) )

/**
 * Group Array by key. Root keys of a resulting array is value
 * of specified key.
 *
 * @param      {Array}   src     The source array
 * @param      {String}  key     The by key to group by
 * @return     {Object}          Object with groupped objects as values
 */
const grpBy = (src, key) => src.reduce((a, e) => (
  (a[e[key]] = a[e[key]] || []).push(rmProp(e, key)),  a
), {});

/**
 * Collapse array of object if it consists of only object with single value.
 * Replace it by the rest value.
 */
const blowObj = obj => Array.isArray(obj) && obj.length === 1 && Object.values(obj[0]).length === 1 ? Object.values(obj[0])[0] : obj;

/**
 * Recoursive groupping with list of keys. `keyList` may be an array
 * of key names or comma separated list of key names whom UNIQUE values will
 * becomes the keys of the resulting object.
 */
const grpByReal = function (src, keyList) {
  const [key, ...rest] = Array.isArray(keyList) ? keyList : String(keyList).trim().split(/\s*,\s*/);
  const res = key ? grpBy(src, key) : [...src];
  if (rest.length) {
for (const k in res) {
  res[k] = grpByReal(res[k], rest)
}
  } else {
for (const k in res) {
  res[k] = blowObj(res[k])
}
  }
  return res;
}

console.log( JSON.stringify( grpByReal(inputArray, 'Phase, Step, Task'), null, 2 ) );


5
groupByArray(xs, key) {
    return xs.reduce(function (rv, x) {
        let v = key instanceof Function ? key(x) : x[key];
        let el = rv.find((r) => r && r.key === v);
        if (el) {
            el.values.push(x);
        }
        else {
            rv.push({
                key: v,
                values: [x]
            });
        }
        return rv;
    }, []);
}

อันนี้ส่งออกอาร์เรย์


4

ตามคำตอบก่อนหน้า

const groupBy = (prop) => (xs) =>
  xs.reduce((rv, x) =>
    Object.assign(rv, {[x[prop]]: [...(rv[x[prop]] || []), x]}), {});

และมันก็ดีกว่าที่จะดูด้วยไวยากรณ์การกระจายวัตถุหากสภาพแวดล้อมของคุณรองรับ

const groupBy = (prop) => (xs) =>
  xs.reduce((acc, x) => ({
    ...acc,
    [ x[ prop ] ]: [...( acc[ x[ prop ] ] || []), x],
  }), {});

ที่นี่ตัวลดของเราใช้ค่าส่งคืนที่เกิดขึ้นบางส่วน (เริ่มต้นด้วยวัตถุที่ว่างเปล่า) และส่งคืนวัตถุที่ประกอบด้วยสมาชิกที่กระจายออกไปของค่าตอบแทนก่อนหน้าพร้อมกับสมาชิกใหม่ที่มีการคำนวณคีย์จากมูลค่าปัจจุบันของผู้propและค่าของมันคือรายการของค่าทั้งหมดสำหรับ prop นั้นพร้อมกับค่าปัจจุบัน


3

Array.prototype.groupBy = function (groupingKeyFn) {
    if (typeof groupingKeyFn !== 'function') {
        throw new Error("groupBy take a function as only parameter");
    }
    return this.reduce((result, item) => {
        let key = groupingKeyFn(item);
        if (!result[key])
            result[key] = [];
        result[key].push(item);
        return result;
    }, {});
}

var a = [
	{type: "video", name: "a"},
  {type: "image", name: "b"},
  {type: "video", name: "c"},
  {type: "blog", name: "d"},
  {type: "video", name: "e"},
]
console.log(a.groupBy((item) => item.type));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


3

นี่เป็นวิธีที่น่ารังเกียจและอ่านยากโดยใช้ ES6:

export default (arr, key) => 
  arr.reduce(
    (r, v, _, __, k = v[key]) => ((r[k] || (r[k] = [])).push(v), r),
    {}
  );

สำหรับผู้ที่ถามว่ามันใช้งานได้อย่างไรนี่เป็นคำอธิบาย:

  • ในทั้ง=>คุณมีฟรีreturn

  • Array.prototype.reduceฟังก์ชั่นใช้เวลาถึง 4 พารามิเตอร์ นั่นเป็นสาเหตุที่มีการเพิ่มพารามิเตอร์ตัวที่ห้าเพื่อให้เราสามารถประกาศตัวแปรราคาถูกสำหรับกลุ่ม (k) ที่ระดับการประกาศพารามิเตอร์โดยใช้ค่าเริ่มต้น (ใช่นี่คือเวทมนตร์)

  • หากกลุ่มปัจจุบันของเราไม่มีอยู่ในการวนซ้ำก่อนหน้านี้เราจะสร้างอาร์เรย์ว่างใหม่((r[k] || (r[k] = []))ซึ่งจะประเมินค่าไปยังนิพจน์ซ้ายสุดในคำอื่น ๆ อาร์เรย์ที่มีอยู่หรืออาร์เรย์ว่างนี่คือสาเหตุที่มีทันทีpushหลังจากนิพจน์นั้นเพราะ วิธีใดคุณจะได้รับอาร์เรย์

  • เมื่อมีตัวดำเนินการreturnเครื่องหมายจุลภาค,จะยกเลิกค่าซ้ายสุดคืนกลุ่มก่อนหน้า tweaked สำหรับสถานการณ์นี้

รุ่นที่เข้าใจง่ายกว่าซึ่งทำเช่นเดียวกันคือ:

export default (array, key) => 
  array.reduce((previous, currentItem) => {
    const group = currentItem[key];
    if (!previous[group]) previous[group] = [];
    previous[group].push(currentItem);
    return previous;
  }, {});

2
คุณอยากจะอธิบายเรื่องนี้บ้างไหมมันใช้งานได้ดี
Nuwan Dammika

@NuwanDammika - ในทั้งสอง => คุณมี "return" ฟรี - ฟังก์ชั่นการลดใช้พารามิเตอร์สูงสุด 4 ตัว นั่นเป็นเหตุผลที่เพิ่มพารามิเตอร์ที่ห้าเพื่อให้เราสามารถประกาศตัวแปรราคาถูกสำหรับกลุ่ม (k) - หากค่าก่อนหน้าไม่มีกลุ่มปัจจุบันของเราเราจะสร้างกลุ่มว่างใหม่ ((r [k] || (r [k] = [])) สิ่งนี้จะประเมินการแสดงออกทางซ้ายสุดมิฉะนั้นอาร์เรย์หรือ นี่เป็นสาเหตุที่ทำให้เกิดการแสดงออกอย่างฉับพลันหลังจากนั้น - เมื่อมีการคืนค่าผู้ใช้เครื่องหมายจุลภาคจะยกเลิกค่าที่อยู่ทางซ้ายสุดส่งคืนกลุ่มก่อนหน้าที่ถูกปรับเปลี่ยน
darkndream

2

ให้สร้างArray.prototype.groupBy()เครื่องมือทั่วไป เพียงเพื่อความหลากหลายลองใช้ ES6 ความเพ้อฝันผู้ดำเนินการแพร่กระจายสำหรับรูปแบบ Haskellesque บางอย่างในวิธีแบบเรียกซ้ำ นอกจากนี้ให้เราArray.prototype.groupBy()ยอมรับการโทรกลับซึ่งใช้รายการ ( e) ดัชนี ( i) และอาร์เรย์ที่ใช้ ( a) เป็นอาร์กิวเมนต์

Array.prototype.groupBy = function(cb){
                            return function iterate([x,...xs], i = 0, r = [[],[]]){
                                     cb(x,i,[x,...xs]) ? (r[0].push(x), r)
                                                       : (r[1].push(x), r);
                                     return xs.length ? iterate(xs, ++i, r) : r;
                                   }(this);
                          };

var arr = [0,1,2,3,4,5,6,7,8,9],
    res = arr.groupBy(e => e < 5);
console.log(res);


2

คำตอบของ Ceasar นั้นดี แต่ใช้ได้เฉพาะกับคุณสมบัติภายในขององค์ประกอบภายในอาร์เรย์ (ความยาวในกรณีของสตริง)

การติดตั้งใช้งานได้เช่น: ลิงค์นี้

const groupBy = function (arr, f) {
    return arr.reduce((out, val) => {
        let by = typeof f === 'function' ? '' + f(val) : val[f];
        (out[by] = out[by] || []).push(val);
        return out;
    }, {});
};

หวังว่านี่จะช่วย ...


2

จาก @mortb คำตอบ @jmarceli และจากการโพสต์นี้ ,

ฉันใช้ประโยชน์จากJSON.stringify()การเป็นตัวตนของค่าความเป็นส่วนตัวหลายคอลัมน์โดย

โดยไม่มีบุคคลที่สาม

function groupBy(list, keyGetter) {
    const map = new Map();
    list.forEach((item) => {
        const key = keyGetter(item);
        if (!map.has(key)) {
            map.set(key, [item]);
        } else {
            map.get(key).push(item);
        }
    });
    return map;
}

const pets = [
    {type:"Dog", age: 3, name:"Spot"},
    {type:"Cat", age: 3, name:"Tiger"},
    {type:"Dog", age: 4, name:"Rover"}, 
    {type:"Cat", age: 3, name:"Leo"}
];

const grouped = groupBy(pets,
pet => JSON.stringify({ type: pet.type, age: pet.age }));

console.log(grouped);

กับLodashบุคคลที่สาม

const pets = [
    {type:"Dog", age: 3, name:"Spot"},
    {type:"Cat", age: 3, name:"Tiger"},
    {type:"Dog", age: 4, name:"Rover"}, 
    {type:"Cat", age: 3, name:"Leo"}
];

let rslt = _.groupBy(pets, pet => JSON.stringify(
 { type: pet.type, age: pet.age }));

console.log(rslt);

keyGetter ส่งคืน undefined
Asbar Ali

@AsbarAli ฉันทดสอบตัวอย่างด้วยคอนโซลของ Chrome - รุ่น 66.0.3359.139 (รุ่นทางการ) (64 บิต) และทุกอย่างทำงานได้ดี คุณช่วยกรุณาใส่จุดพักการดีบักและดูว่าทำไม keyGetter ไม่ได้กำหนด อาจเป็นเพราะเบราว์เซอร์เวอร์ชั่น
Pranithan T.

2

ES6 เวอร์ชั่นตามที่มีการสนับสนุนสำหรับฟังก์ชั่นreduceiteratee

ใช้งานได้ตามที่คาดหวังหากiterateeไม่ได้จัดเตรียมฟังก์ชัน:

const data = [{id: 1, score: 2},{id: 1, score: 3},{id: 2, score: 2},{id: 2, score: 4}]

const group = (arr, k) => arr.reduce((r, c) => (r[c[k]] = [...r[c[k]] || [], c], r), {});

const groupBy = (arr, k, fn = () => true) => 
  arr.reduce((r, c) => (fn(c[k]) ? r[c[k]] = [...r[c[k]] || [], c] : null, r), {});

console.log(group(data, 'id'))     // grouping via `reduce`
console.log(groupBy(data, 'id'))   // same result if `fn` is omitted
console.log(groupBy(data, 'score', x => x > 2 )) // group with the iteratee

ในบริบทของคำถาม OP:

const data = [ { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" }, { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" }, { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" }, { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" }, { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" }, { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" }, { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" }, { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" } ]

const groupBy = (arr, k) => arr.reduce((r, c) => (r[c[k]] = [...r[c[k]] || [], c], r), {});
const groupWith = (arr, k, fn = () => true) => 
  arr.reduce((r, c) => (fn(c[k]) ? r[c[k]] = [...r[c[k]] || [], c] : null, r), {});

console.log(groupBy(data, 'Phase'))
console.log(groupWith(data, 'Value', x => x > 30 ))  // group by `Value` > 30

อีกรุ่นES6ซึ่งย้อนกลับการจัดกลุ่มและใช้valuesเป็นkeysและkeysเป็นgrouped values:

const data = [{A: "1"}, {B: "10"}, {C: "10"}]

const groupKeys = arr => 
  arr.reduce((r,c) => (Object.keys(c).map(x => r[c[x]] = [...r[c[x]] || [], x]),r),{});

console.log(groupKeys(data))

หมายเหตุ:ฟังก์ชั่นจะโพสต์ในรูปแบบสั้น ๆ ของพวกเขา (หนึ่งบรรทัด) เพื่อความกะทัดรัดและเพื่อเชื่อมโยงกับแนวคิด คุณสามารถขยายและเพิ่มการตรวจสอบข้อผิดพลาดเพิ่มเติม ฯลฯ


2

ฉันจะตรวจสอบข้อความ-js groupByดูเหมือนว่าจะทำสิ่งที่คุณกำลังมองหา มันยัง:

  • มีประสิทธิภาพมาก ( มาตรฐานประสิทธิภาพ)
  • เขียนใน typescript เพื่อให้การพิมพ์ทั้งหมดรวมอยู่ด้วย
  • ไม่บังคับใช้วัตถุคล้ายอาร์เรย์ของบุคคลที่สาม
import { Reducers } from 'declarative-js';
import groupBy = Reducers.groupBy;
import Map = Reducers.Map;

const data = [
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
];

data.reduce(groupBy(element=> element.Step), Map());
data.reduce(groupBy('Step'), Map());

1
let groupbyKeys = function(arr, ...keys) {
  let keysFieldName = keys.join();
  return arr.map(ele => {
    let keysField = {};
    keysField[keysFieldName] = keys.reduce((keyValue, key) => {
      return keyValue + ele[key]
    }, "");
    return Object.assign({}, ele, keysField);
  }).reduce((groups, ele) => {
    (groups[ele[keysFieldName]] = groups[ele[keysFieldName]] || [])
      .push([ele].map(e => {
        if (keys.length > 1) {
          delete e[keysFieldName];
        }
        return e;
    })[0]);
    return groups;
  }, {});
};

console.log(groupbyKeys(array, 'Phase'));
console.log(groupbyKeys(array, 'Phase', 'Step'));
console.log(groupbyKeys(array, 'Phase', 'Step', 'Task'));

1

นี่คือรุ่น ES6 ที่จะไม่ทำลายสมาชิกโมฆะ

function groupBy (arr, key) {
  return (arr || []).reduce((acc, x = {}) => ({
    ...acc,
    [x[key]]: [...acc[x[key]] || [], x]
  }), {})
}

1

เพียงเพิ่มคำตอบของ Scott Sauyet บางคนถามในความคิดเห็นวิธีใช้ฟังก์ชันของเขาเพื่อจัดกลุ่มตามค่า 1, ค่า 2, ฯลฯ แทนที่จะจัดกลุ่มเพียงค่าเดียว

สิ่งที่ต้องทำคือแก้ไขฟังก์ชันผลรวมของเขา:

DataGrouper.register("sum", function(item) {
    return _.extend({}, item.key,
        {VALUE1: _.reduce(item.vals, function(memo, node) {
        return memo + Number(node.VALUE1);}, 0)},
        {VALUE2: _.reduce(item.vals, function(memo, node) {
        return memo + Number(node.VALUE2);}, 0)}
    );
});

ปล่อยให้หลัก (DataGrouper) ไม่เปลี่ยนแปลง:

var DataGrouper = (function() {
    var has = function(obj, target) {
        return _.any(obj, function(value) {
            return _.isEqual(value, target);
        });
    };

    var keys = function(data, names) {
        return _.reduce(data, function(memo, item) {
            var key = _.pick(item, names);
            if (!has(memo, key)) {
                memo.push(key);
            }
            return memo;
        }, []);
    };

    var group = function(data, names) {
        var stems = keys(data, names);
        return _.map(stems, function(stem) {
            return {
                key: stem,
                vals:_.map(_.where(data, stem), function(item) {
                    return _.omit(item, names);
                })
            };
        });
    };

    group.register = function(name, converter) {
        return group[name] = function(data, names) {
            return _.map(group(data, names), converter);
        };
    };

    return group;
}());

1

ด้วยคุณสมบัติการเรียงลำดับ

export const groupBy = function groupByArray(xs, key, sortKey) {
      return xs.reduce(function(rv, x) {
        let v = key instanceof Function ? key(x) : x[key];
        let el = rv.find(r => r && r.key === v);

        if (el) {
          el.values.push(x);
          el.values.sort(function(a, b) {
            return a[sortKey].toLowerCase().localeCompare(b[sortKey].toLowerCase());
          });
        } else {
          rv.push({ key: v, values: [x] });
        }

        return rv;
      }, []);
    };

ตัวอย่าง:

var state = [
    {
      name: "Arkansas",
      population: "2.978M",
      flag:
  "https://upload.wikimedia.org/wikipedia/commons/9/9d/Flag_of_Arkansas.svg",
      category: "city"
    },{
      name: "Crkansas",
      population: "2.978M",
      flag:
        "https://upload.wikimedia.org/wikipedia/commons/9/9d/Flag_of_Arkansas.svg",
      category: "city"
    },
    {
      name: "Balifornia",
      population: "39.14M",
      flag:
        "https://upload.wikimedia.org/wikipedia/commons/0/01/Flag_of_California.svg",
      category: "city"
    },
    {
      name: "Florida",
      population: "20.27M",
      flag:
        "https://upload.wikimedia.org/wikipedia/commons/f/f7/Flag_of_Florida.svg",
      category: "airport"
    },
    {
      name: "Texas",
      population: "27.47M",
      flag:
        "https://upload.wikimedia.org/wikipedia/commons/f/f7/Flag_of_Texas.svg",
      category: "landmark"
    }
  ];
console.log(JSON.stringify(groupBy(state,'category','name')));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.