javascript ऑब्जेक्ट्स की जावास्क्रिप्ट सरणी पर समूहबद्ध करने का सबसे प्रभावी तरीका क्या है?



जावास्क्रिप्ट उद्देश्य सवाल और हिंदी में जवाब (18)

एक सरणी में वस्तुओं को समूहबद्ध करने का सबसे प्रभावी तरीका क्या है?

उदाहरण के लिए, वस्तुओं की इस सरणी को दिया गया है:

[ 
    { 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" }
]

मैं इस जानकारी को एक टेबल में प्रदर्शित कर रहा हूं। मैं विभिन्न तरीकों को समूहबद्ध करना चाहता हूं, लेकिन मैं मानों को जोड़ना चाहता हूं।

मैं अंडरस्कोर.जेएस का उपयोग अपने ग्रुपबी फ़ंक्शन के लिए कर रहा हूं, जो सहायक है, लेकिन पूरी चाल नहीं करता है, क्योंकि मैं नहीं चाहता कि वे "विभाजन करें" लेकिन "विलय" करें, विधि के अनुसार SQL group by तरह।

जो मैं खोज रहा हूं वह कुल विशिष्ट मानों (यदि अनुरोध किया गया हो) में सक्षम होगा।

तो अगर मैंने ग्रुपबी Phase , तो मैं प्राप्त करना चाहता हूं:

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

और अगर मैंने समूही 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 का उपयोग करने के लिए चिपकना चाहिए, और फिर परिणामस्वरूप ऑब्जेक्ट के माध्यम से खुद को कुल करने के लिए लूप करना चाहिए?

https://ffff65535.com


@mortb से @ jmarceli उत्तर और इस पोस्ट से ,

मैं JSON.stringify() का लाभ लेता हूं ताकि JSON.stringify() के 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);

लोडाश तीसरे पक्ष के साथ

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);

ES6 के साथ:

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

आइए एक सामान्य Array.prototype.groupBy() टूल उत्पन्न करें। बस विविधता के लिए चलिए एक रिकर्सिव दृष्टिकोण पर मेल खाने वाले कुछ हास्केलस्क पैटर्न के लिए ईएस 6 फैंसी स्प्रेड ऑपरेटर का उपयोग करते हैं। साथ ही हमारे 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);


आप array.reduce() से एक ES6 Map बना सकते हैं।

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

इसके अन्य समाधानों पर कुछ फायदे हैं:

  • इसे किसी भी पुस्तकालय की आवश्यकता नहीं है (उदाहरण के विपरीत _.groupBy() )
  • आपको ऑब्जेक्ट की बजाय जावास्क्रिप्ट Map मिलता है (उदाहरण के लिए _.groupBy() द्वारा लौटाया गया है)। इसमें बहुत से लाभ हैं , जिनमें निम्न शामिल हैं:
    • यह उस क्रम को याद करता है जिसमें आइटम पहले जोड़े गए थे,
    • कुंजी केवल तारों के बजाय किसी भी प्रकार का हो सकता है।
  • एक Map एक और उपयोगी परिणाम है कि सरणी की एक सरणी। लेकिन अगर आप सरणी की सरणी चाहते हैं, तो आप Array.from(groupedMap.entries()) ( [key, group array] जोड़े की [key, group array] ) या Array.from(groupedMap.values()) लिए कॉल कर सकते हैं सरणी की एक साधारण सरणी)।
  • यह काफी लचीला है; अक्सर, जो भी आप इस मानचित्र के साथ आगे करने की योजना बना रहे थे, वह कमी के हिस्से के रूप में सीधे किया जा सकता है।

आखिरी बिंदु के उदाहरण के रूप में, कल्पना करें कि मेरे पास ऑब्जेक्ट्स की एक सरणी है जिसे मैं आईडी द्वारा एक (उथला) विलय करना चाहता हूं, जैसे:

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()
);

आप इसे निम्नलिखित तरीके से कर सकते हैं। मैंने अभी नई सरणी बनाई है और इसे समूह से वापस कर दिया है। .map फ़ंक्शन द्वारा बस लूपिंग से गणना की गई गणना

var arr = [ 
        { 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 groupBy = (arr, pahse, step='') => {

   var pahseArr = [];
   var resultArr = [];

   arr.map((item)=>{
     var pushed = false;
     pahseArr.map((ele)=>{
       if(ele===item.Phase){
         pushed = true;
       }
     })
     if(!pushed){
       pahseArr.push(item.Phase);
     }     
   })

   pahseArr.map((item)=>{
      var sum = 0;
      arr.map((ele)=>{
        if(ele.Phase===item){
          sum += parseFloat(ele.Value)
        }
      })
      resultArr.push({
        Phase: item,
        Value: sum
      })
   })

   if(step!=''){
     var resultArr = [];


     pahseArr.map((item)=>{
         var stepArr = [];

         arr.map((item2)=>{
           var pushed = false;
           stepArr.map((ele)=>{
             if(ele===item2.Step){
               pushed = true;
             }
           })
           if(!pushed){
             stepArr.push(item2.Step);
           } 
         })

         stepArr.map((item1)=>{
            var sum = 0;
            arr.map((ele)=>{
              if(ele.Step===item1 && ele.Phase===item){
                sum += parseFloat(ele.Value)
              }
            })
            resultArr.push({
              Phase: item,
              Step: item1,
              Value: sum
            })
         })

     })
     return resultArr;
   }   
   return resultArr;

}

console.log(groupBy(arr, 'Phase'));
console.log(groupBy(arr, 'Phase', 'Step'));

उत्परिवर्तन के बिना:

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"]}

बस स्कॉट सॉयेट के answer जोड़ने के लिए, कुछ लोग टिप्पणियों में पूछ रहे थे कि समूह के मूल्य 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)}
    );
});

मुख्य एक (डेटाग्राउपर) अपरिवर्तित छोड़कर:

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;
}());

मैं lodash समूह की जांच करूँगा ऐसा लगता है कि आप वही कर रहे हैं जो आप खोज रहे हैं। यह भी काफी हल्का और वास्तव में सरल है।

बेवकूफ उदाहरण: https://jsfiddle.net/r7szvt5k/

बशर्ते कि आपका सरणी नाम समूह का arr , लॉनाश के साथ बस है:

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

मैंने underscore.js fiddler से इस विधि को उधार लिया

window.helpers=(function (){
    var lookupIterator = function(value) {
        if (value == null){
            return function(value) {
                return value;
            };
        }
        if (typeof value === 'function'){
                return value;
        }
        return function(obj) {
            return obj[value];
        };
    },
    each = function(obj, iterator, context) {
        var breaker = {};
        if (obj == null) return obj;
        if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {
            obj.forEach(iterator, context);
        } else if (obj.length === +obj.length) {
            for (var i = 0, length = obj.length; i < length; i++) {
                if (iterator.call(context, obj[i], i, obj) === breaker) return;
            }
        } else {
            var keys = []
            for (var key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) keys.push(key)
            for (var i = 0, length = keys.length; i < length; i++) {
                if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
            }
        }
        return obj;
    },
    // An internal function used for aggregate "group by" operations.
    group = function(behavior) {
        return function(obj, iterator, context) {
            var result = {};
            iterator = lookupIterator(iterator);
            each(obj, function(value, index) {
                var key = iterator.call(context, value, index, obj);
                behavior(result, key, value);
            });
            return result;
        };
    };

    return {
      groupBy : group(function(result, key, value) {
        Object.prototype.hasOwnProperty.call(result, key) ? result[key].push(value) :              result[key] = [value];
        })
    };
})();

var arr=[{a:1,b:2},{a:1,b:3},{a:1,b:1},{a:1,b:2},{a:1,b:3}];
 console.dir(helpers.groupBy(arr,"b"));
 console.dir(helpers.groupBy(arr,function (el){
   return el.b>2;
 }));

यदि आप बाहरी पुस्तकालयों से बचना चाहते हैं, तो आप 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"]}

यद्यपि प्रश्न के कुछ जवाब हैं और उत्तर जटिल पर थोड़ा सा दिखते हैं, लेकिन मैं समूह-द्वारा के लिए वेनिला जावास्क्रिप्ट का उपयोग करने का सुझाव देता हूं।

इस समाधान में एक फ़ंक्शन है जो डेटा सरणी के साथ एक सरणी लेता है और रिटर्न col लिए एक प्रॉपर्टी नाम और मूल्य value गिनने के लिए एक संपत्ति का नाम लेता है।

फ़ंक्शन किसी ऑब्जेक्ट पर निर्भर करता है, जो परिणाम के लिए हैश तालिका के रूप में कार्य करता है।

function groupBy(array, col, value) {
    var r = [], o = {};
    array.forEach(function (a) {
        if (!o[a[col]]) {
            o[a[col]] = {};
            o[a[col]][col] = a[col];
            o[a[col]][value] = 0;
            r.push(o[a[col]]);
        }
        o[a[col]][value] += +a[value];
    });
    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" }];

document.write('<pre>' + JSON.stringify(groupBy(data, 'Phase', 'Value'), 0, 4) + '</pre>');


यह शायद linq साथ अधिक आसानी से किया जाता है, जिसका उद्देश्य जावास्क्रिप्ट ( DEMO ) में LINQ का सही कार्यान्वयन होना है:

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();

यहां एक ईएस 6 संस्करण है जो शून्य सदस्यों पर नहीं टूट जाएगा

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

सीज़र का जवाब अच्छा है, लेकिन केवल सरणी के अंदर तत्वों (स्ट्रिंग के मामले में लंबाई) के आंतरिक गुणों के लिए काम करता है।

यह कार्यान्वयन इस तरह काम करता है: यह लिंक

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;
    }, {});
};

उम्मीद है की यह मदद करेगा...


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>


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]
        };
    });
};

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;
    }, []);
}

यह एक सरणी आउटपुट करता है।


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'));





underscore.js