अंडरस्कोर: सॉर्टबाय () कई विशेषताओं के आधार पर


115

मैं कई विशेषताओं के आधार पर वस्तुओं के साथ एक सरणी को सॉर्ट करने की कोशिश कर रहा हूं। यानी यदि पहली विशेषता दो वस्तुओं के बीच समान हो तो दूसरी विशेषता का उपयोग दो वस्तुओं को प्राप्त करने के लिए किया जाना चाहिए। उदाहरण के लिए, निम्नलिखित सरणी पर विचार करें:

var patients = [
             [{name: 'John', roomNumber: 1, bedNumber: 1}],
             [{name: 'Lisa', roomNumber: 1, bedNumber: 2}],
             [{name: 'Chris', roomNumber: 2, bedNumber: 1}],
             [{name: 'Omar', roomNumber: 3, bedNumber: 1}]
               ];

roomNumberविशेषता द्वारा इन्हें क्रमबद्ध करना मैं निम्नलिखित कोड का उपयोग करूंगा:

var sortedArray = _.sortBy(patients, function(patient) {
    return patient[0].roomNumber;
});

यह ठीक काम करता है, लेकिन मैं कैसे आगे बढ़ूं ताकि 'जॉन' और 'लिसा' को ठीक से सुलझाया जाए?

जवाबों:


250

sortBy यह कहता है कि यह एक स्थिर सॉर्ट एल्गोरिथ्म है, इसलिए आपको अपनी दूसरी प्रॉपर्टी को पहले सॉर्ट करने में सक्षम होना चाहिए, फिर अपनी पहली प्रॉपर्टी के अनुसार फिर से छाँटना चाहिए, जैसे:

var sortedArray = _(patients).chain().sortBy(function(patient) {
    return patient[0].name;
}).sortBy(function(patient) {
    return patient[0].roomNumber;
}).value();

जब दूसरे को sortByपता चलता है कि जॉन और लिसा के पास एक ही कमरा नंबर है, तो यह उन्हें उसी क्रम में रखेगा, जो उन्हें पहले मिला था, जो कि sortBy"लिसा, जॉन" के लिए पहला सेट था।


12
एक ब्लॉग पोस्ट है जो इस पर फैलता है और इसमें आरोही और अवरोही गुणों को छाँटने की अच्छी जानकारी शामिल है।
एलेक्स सी

4
जंजीर के प्रकार का एक सरल समाधान यहां पाया जा सकता है । निष्पक्ष होने के लिए, ऐसा लगता है कि ब्लॉग पोस्ट इन उत्तरों के दिए जाने के बाद लिखा गया था, लेकिन इसने मुझे यह पता लगाने में मदद की कि उपरोक्त उत्तर में कोड का उपयोग करने की कोशिश करने और असफल होने के बाद।
माइक डेवेनी

1
आपको यकीन है कि रोगी [0] .name और रोगी [1] .roomNumber में सूचकांक होना चाहिए? रोगी एक सरणी नहीं है ...
StinkyCat

[0]इंडेक्सर की आवश्यकता है क्योंकि मूल उदाहरण में, है patientsसरणियों की एक सरणी है। यही कारण है कि एक और टिप्पणी में उल्लिखित ब्लॉग पोस्ट में "सरल समाधान" यहां काम नहीं करेगा।
रोरी मैकलेओड

1
: @ac_fire यहाँ है कि अब प्रयोग में नहीं लिंक का एक संग्रह है archive.is/tiatQ
Lustig

52

इन मामलों में मैं कभी-कभी एक हैक करने की चाल का उपयोग करता हूं: गुणों को इस तरह से संयोजित करें कि परिणाम छंटनी होगी:

var sortedArray = _.sortBy(patients, function(patient) {
  return [patient[0].roomNumber, patient[0].name].join("_");
});

हालाँकि, जैसा कि मैंने कहा, यह बहुत हैकरी है। इसे ठीक से करने के लिए आप वास्तव में कोर जावास्क्रिप्ट sortविधि का उपयोग करना चाहेंगे :

patients.sort(function(x, y) {
  var roomX = x[0].roomNumber;
  var roomY = y[0].roomNumber;
  if (roomX !== roomY) {
    return compare(roomX, roomY);
  }
  return compare(x[0].name, y[0].name);
});

// General comparison function for convenience
function compare(x, y) {
  if (x === y) {
    return 0;
  }
  return x > y ? 1 : -1;
}

बेशक, यह आपके एरे को जगह में सॉर्ट करेगा। यदि आप एक हल की हुई प्रति चाहते हैं (जैसे _.sortByकि आप देंगे), पहले सरणी को क्लोन करें:

function sortOutOfPlace(sequence, sorter) {
  var copy = _.clone(sequence);
  copy.sort(sorter);
  return copy;
}

बोरियत से बाहर, मैंने इसके लिए एक सामान्य समाधान (किसी भी मनमाने ढंग से संख्या के आधार पर क्रमबद्ध करने के लिए) लिखा है: एक नज़र


इस समाधान के लिए बहुत बहुत धन्यवाद दूसरे का उपयोग करते हुए समाप्त हो गया क्योंकि मेरी विशेषताएँ तार और संख्या दोनों हो सकती हैं। तो सरणियों को सॉर्ट करने के लिए एक सरल देशी तरीका प्रतीत नहीं होता है?
क्रिश्चियन आर

3
return [patient[0].roomNumber, patient[0].name];बिना पर्याप्त क्यों नहीं है join?
सीसाबा तोथ

1
आपके सामान्य समाधान का लिंक टूटा हुआ प्रतीत होता है (या शायद मैं इसे हमारे प्रॉक्सी सर्वर के माध्यम से एक्सेस करने में असमर्थ हूं)। क्या आप इसे यहाँ पोस्ट कर सकते हैं?
ज़ेव स्पिट्ज

इसके अलावा, कैसे करता है compare- संभाल मूल्यों जो आदिम मान नहीं हैं undefined, nullया सादे वस्तुओं?
ज़ेव स्पिट्ज

FYI करें यह हैक केवल तभी काम करता है जब आप यह सुनिश्चित करते हैं कि सरणी में सभी आइटम्स के लिए प्रत्येक मान की स्ट्रोंग लंबाई समान है।
miex

32

मुझे पता है कि मुझे पार्टी में देर हो गई है, लेकिन मैं उन लोगों के लिए एक क्लीन-एर और क्विक-एर समाधान की आवश्यकता को जोड़ना चाहता था जो पहले से ही सुझाए गए थे। आप सबसे महत्वपूर्ण संपत्ति के लिए कम से कम महत्वपूर्ण संपत्ति के क्रम में कॉल सॉर्ट कर सकते हैं। कोड में नीचे मैं के अनुसार क्रमबद्ध रोगियों की एक नई सरणी बनाने के नाम के भीतर RoomNumber मूल सरणी कहा जाता है से रोगियों

var sortedPatients = _.chain(patients)
  .sortBy('Name')
  .sortBy('RoomNumber')
  .value();

4
भले ही आप देर से हो फिर भी आप सही हैं :) धन्यवाद!
एलन जिकामु

3
बहुत अच्छा, बहुत साफ।
जेसन तुरान

11

btw रोगियों के लिए अपने initializer थोड़ा अजीब है, है ना? आप इस चर को आरंभ क्यों नहीं करते हैं, क्योंकि यह वस्तुओं का एक वास्तविक सरणी है -आप इसे _.flatten () का उपयोग करके कर सकते हैं और एकल ऑब्जेक्ट के सरणियों के सरणी के रूप में नहीं, शायद यह टाइपो मुद्दा है):

var patients = [
        {name: 'Omar', roomNumber: 3, bedNumber: 1},
        {name: 'John', roomNumber: 1, bedNumber: 1},
        {name: 'Chris', roomNumber: 2, bedNumber: 1},
        {name: 'Lisa', roomNumber: 1, bedNumber: 2},
        {name: 'Kiko', roomNumber: 1, bedNumber: 2}
        ];

मैंने सूची को अलग तरह से क्रमबद्ध किया और लिसा के बिस्तर में किको जोड़ दिया; सिर्फ मनोरंजन के लिए और देखें कि क्या बदलाव होंगे ...

var sorted = _(patients).sortBy( 
                    function(patient){
                       return [patient.roomNumber, patient.bedNumber, patient.name];
                    });

छांटे गए निरीक्षण और आप इसे देखेंगे

[
{bedNumber: 1, name: "John", roomNumber: 1}, 
{bedNumber: 2, name: "Kiko", roomNumber: 1}, 
{bedNumber: 2, name: "Lisa", roomNumber: 1}, 
{bedNumber: 1, name: "Chris", roomNumber: 2}, 
{bedNumber: 1, name: "Omar", roomNumber: 3}
]

तो मेरा जवाब है: अपने कॉलबैक फ़ंक्शन में एक सरणी का उपयोग करें यह डैन ताओ के उत्तर के समान है , मैं सिर्फ जॉइन को भूल जाता हूं (शायद इसलिए कि मैंने अद्वितीय आइटम के सरणियों का सरणी हटा दिया है))
आपके डेटा संरचना का उपयोग करना, फिर यह होने वाला :

var sorted = _(patients).chain()
                        .flatten()
                        .sortBy( function(patient){
                              return [patient.roomNumber, 
                                     patient.bedNumber, 
                                     patient.name];
                        })
                        .value();

और एक टेस्टलोड दिलचस्प होगा ...


गंभीरता से, यह जवाब है
Radek Ducho

7

इन उत्तरों में से कोई भी एक तरह से कई क्षेत्रों का उपयोग करने के लिए एक सामान्य उद्देश्य विधि के रूप में आदर्श नहीं है। उपरोक्त सभी दृष्टिकोण अक्षम हैं क्योंकि उन्हें या तो सरणी को कई बार क्रमबद्ध करने की आवश्यकता होती है (जो, एक बड़ी पर्याप्त सूची में चीजों को बहुत कम कर सकती है) या वे बहुत बड़ी मात्रा में कचरा वस्तुओं को उत्पन्न करते हैं जिन्हें वीएम को साफ करने की आवश्यकता होगी (और अंततः धीमा करना होगा कार्यक्रम नीचे)।

यहाँ एक समाधान है कि तेजी से, कुशल है, आसानी से रिवर्स छँटाई की अनुमति देता है, और साथ इस्तेमाल किया जा सकता underscoreया lodash, या के साथ सीधेArray.sort

सबसे महत्वपूर्ण हिस्सा compositeComparatorविधि है, जो तुलनित्र कार्यों की एक सरणी लेता है और एक नया समग्र तुलनित्र फ़ंक्शन देता है।

/**
 * Chains a comparator function to another comparator
 * and returns the result of the first comparator, unless
 * the first comparator returns 0, in which case the
 * result of the second comparator is used.
 */
function makeChainedComparator(first, next) {
  return function(a, b) {
    var result = first(a, b);
    if (result !== 0) return result;
    return next(a, b);
  }
}

/**
 * Given an array of comparators, returns a new comparator with
 * descending priority such that
 * the next comparator will only be used if the precending on returned
 * 0 (ie, found the two objects to be equal)
 *
 * Allows multiple sorts to be used simply. For example,
 * sort by column a, then sort by column b, then sort by column c
 */
function compositeComparator(comparators) {
  return comparators.reduceRight(function(memo, comparator) {
    return makeChainedComparator(comparator, memo);
  });
}

आपको उन क्षेत्रों की तुलना करने के लिए एक तुलनित्र फ़ंक्शन की भी आवश्यकता होगी जिन्हें आप सॉर्ट करना चाहते हैं। naturalSortसमारोह एक विशेष क्षेत्र को देखते हुए एक तुलनित्र पैदा करेगा। रिवर्स सॉर्टिंग के लिए एक तुलनित्र लिखना तुच्छ भी है।

function naturalSort(field) {
  return function(a, b) {
    var c1 = a[field];
    var c2 = b[field];
    if (c1 > c2) return 1;
    if (c1 < c2) return -1;
    return 0;
  }
}

(अब तक के सभी कोड पुन: प्रयोज्य हैं और उपयोगिता मॉड्यूल में रखे जा सकते हैं, उदाहरण के लिए)

अगला, आपको समग्र तुलनित्र बनाने की आवश्यकता है। हमारे उदाहरण के लिए, यह इस तरह दिखेगा:

var cmp = compositeComparator([naturalSort('roomNumber'), naturalSort('name')]);

यह कमरा नंबर, नाम के बाद क्रमबद्ध होगा। अतिरिक्त सॉर्ट मानदंड जोड़ना तुच्छ है और यह सॉर्ट के प्रदर्शन को प्रभावित नहीं करता है।

var patients = [
 {name: 'John', roomNumber: 3, bedNumber: 1},
 {name: 'Omar', roomNumber: 2, bedNumber: 1},
 {name: 'Lisa', roomNumber: 2, bedNumber: 2},
 {name: 'Chris', roomNumber: 1, bedNumber: 1},
];

// Sort using the composite
patients.sort(cmp);

console.log(patients);

निम्नलिखित लौटाता है

[ { name: 'Chris', roomNumber: 1, bedNumber: 1 },
  { name: 'Lisa', roomNumber: 2, bedNumber: 2 },
  { name: 'Omar', roomNumber: 2, bedNumber: 1 },
  { name: 'John', roomNumber: 3, bedNumber: 1 } ]

इस विधि को पसंद करने का कारण यह है कि यह खेतों की एक मनमानी संख्या पर तेजी से छंटाई की अनुमति देता है, बहुत अधिक कचरा उत्पन्न नहीं करता है या सॉर्ट के अंदर स्ट्रिंग संघनन करता है और आसानी से उपयोग किया जा सकता है ताकि ऑर्डर कॉलम प्राकृतिक होने के दौरान कुछ छंटे हुए हैं। छांटते हैं।



2

शायद ये अंडरस्कोर.जेएस या सिर्फ जावास्क्रिप्ट इंजन तब अलग होते हैं जब ये उत्तर लिखे जाते थे, लेकिन मैं इसे हल करने में सक्षम था, बस तरह की चाबियों का एक सरणी लौटाकर।

var input = [];

for (var i = 0; i < 20; ++i) {
  input.push({
    a: Math.round(100 * Math.random()),
    b: Math.round(3 * Math.random())
  })
}

var output = _.sortBy(input, function(o) {
  return [o.b, o.a];
});

// output is now sorted by b ascending, a ascending

कार्रवाई में, कृपया इस फिडेल को देखें: https://jsfiddle.net/mikeular/xenu3u91/


2

बस उन गुणों की एक सरणी लौटाएं जिन्हें आप क्रमबद्ध करना चाहते हैं:

ईएस 6 सिंटेक्स

var sortedArray = _.sortBy(patients, patient => [patient[0].name, patient[1].roomNumber])

ES5 सिंटेक्स

var sortedArray = _.sortBy(patients, function(patient) { 
    return [patient[0].name, patient[1].roomNumber]
})

यह किसी संख्या को स्ट्रिंग में बदलने का कोई दुष्प्रभाव नहीं है।


1

आप इट्रेटर द्वारा उन गुणों को अलग कर सकते हैं जिन्हें आप सॉर्ट करना चाहते हैं:

return [patient[0].roomNumber,patient[0].name].join('|');

या कुछ समकक्ष।

नोट: जब से आप संख्यात्मक विशेषता रूमबर्न को एक स्ट्रिंग में परिवर्तित कर रहे हैं, तो आपको कुछ करना होगा यदि आपके पास कमरा नंबर> 10. अन्यथा 11 पहले आ जाएगा 2. आप समस्या को हल करने के लिए अग्रणी शून्य के साथ पैड कर सकते हैं, अर्थात 01 के बजाय 1।


1

मुझे लगता है कि आप _.orderByइसके बजाय बेहतर उपयोग करेंगे sortBy:

_.orderBy(patients, ['name', 'roomNumber'], ['asc', 'desc'])

4
क्या आप सुनिश्चित हैं कि आदेश अंडरस्कोर में है? मैं इसे डॉक्स या मेरी .d.ts फ़ाइल में नहीं देख सकता।
ज़ाचरी डॉव

1
अंडरस्कोर में कोई आदेश नहीं है।
AfroMogli

1
_.orderByकाम करता है, लेकिन यह लॉकेट लाइब्रेरी का एक तरीका है, अंडरस्कोर नहीं: lodash.com/docs/4.17.4#orderBy लॉश ज्यादातर अंडरस्कोर के लिए एक ड्रॉप-इन प्रतिस्थापन है, इसलिए यह ओपी के लिए उपयुक्त हो सकता है।
माइक के

0

यदि आप कोणीय का उपयोग कर रहे हैं, तो आप किसी जेएस या सीएसएस हैंडलर को जोड़ने के बजाय html फ़ाइल में इसके नंबर फ़िल्टर का उपयोग कर सकते हैं। उदाहरण के लिए:

  No fractions: <span>{{val | number:0}}</span><br>

उस उदाहरण में, यदि val = 1234567 है, तो इसे प्रदर्शित किया जाएगा

  No fractions: 1,234,567

उदाहरण और आगे के मार्गदर्शन में: https://docs.angularjs.org/api/ng/filter/number

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.