जावास्क्रिप्ट सरणियों का उपयोग करके एक सेट अंतर की गणना करने का सबसे तेज़ या सबसे सुंदर तरीका क्या है?


103

आज्ञा देना Aऔर Bदो सेट होना। मैं उनके बीच सेट अंतर ( या , आपकी पसंद के आधार पर) की गणना करने के लिए वास्तव में तेज़ या सुरुचिपूर्ण तरीके खोज रहा हूं । जैसा कि शीर्षक कहता है, दो सेटों को जावास्क्रिप्ट सरणियों के रूप में संग्रहीत और हेरफेर किया जाता है।A - BA \B

टिप्पणियाँ:

  • गेको-विशिष्ट चाल ठीक हैं
  • मैं मूल कार्यों से चिपके रहना पसंद करूँगा (लेकिन अगर यह तेज़ हो तो मैं एक हल्के पुस्तकालय के लिए खुला हूँ)
  • मैंने देखा है, लेकिन परीक्षण नहीं किया है, JS.Set (पिछले बिंदु देखें)

संपादित करें: मैंने डुप्लिकेट तत्वों वाले सेट के बारे में एक टिप्पणी देखी। जब मैं कहता हूं "सेट" मैं गणितीय परिभाषा की बात कर रहा हूं, जिसका अर्थ है (अन्य चीजों के बीच) जिसमें उनके पास डुप्लिकेट तत्व नहीं हैं।


यह "सेट अंतर" शब्दावली क्या आप उपयोग कर रहे हैं? वह C ++ से है या कुछ और?
जोश स्टोडोला

आपके सेट्स में क्या हैं? आपके द्वारा लक्षित किए जा रहे प्रकार (उदाहरण संख्या) के आधार पर, सेट अंतर की गणना करना वास्तव में तेज़ और सुरुचिपूर्ण हो सकता है । यदि आपके सेट में DOM तत्व होते हैं (कहते हैं), तो आप धीमे indexOfकार्यान्वयन के साथ अटक जाएंगे ।
वर्धमान ताजा

@Crescent: मेरे सेट में नंबर हैं - निर्दिष्ट नहीं करने के लिए क्षमा करें। @ जोश: यह गणित में मानक सेट ऑपरेशन है ( en.wikipedia.org/wiki/Set_%28mathematics%29#Complements )
मैट बॉल


1
@MattBall नहीं, मैंने देखा है। लेकिन जोश का सवाल वैध और अनुत्तरित था इसलिए मैंने इसका जवाब दिया :)
पैट

जवाबों:


173

अगर यह नहीं पता है कि यह सबसे प्रभावी है, लेकिन शायद सबसे कम

A = [1, 2, 3, 4];
B = [1, 3, 4, 7];

diff = A.filter(function(x) { return B.indexOf(x) < 0 })

console.log(diff);

ES6 को अपडेट किया गया:

A = [1, 2, 3, 4];
B = [1, 3, 4, 7];

diff = A.filter(x => !B.includes(x) );

console.log(diff);

8
+1: सबसे कुशल समाधान नहीं है, लेकिन निश्चित रूप से छोटा और पठनीय है
Christoph

10
नोट: array.filter क्रॉस-ब्राउज़र समर्थित नहीं है (जैसे IE में नहीं)। यह @Matt के लिए कोई फर्क नहीं पड़ता क्योंकि उन्होंने कहा कि "गेको-विशिष्ट चाल ठीक है" लेकिन मुझे लगता है कि यह ध्यान देने योग्य है।
एरिक ब्रेकेमियर

44
यह बहुत धीमी है। O (| A | * | B |)
glebm

1
@ EricBréchemier अब यह समर्थित है (IE 9 के बाद से)। Array.prototype.filter एक मानक ECMAScript सुविधा है।
क्वेंटिन रॉय

5
ES6 में, आप :)!B.includes(x)B.indexOf(x) < 0
c24w

86

खैर, 7 साल बाद, ES6 के सेट ऑब्जेक्ट के साथ यह काफी आसान है (लेकिन अभी भी अजगर के रूप में कॉम्पैक्ट नहीं है A - B), और कथित तौर पर indexOfबड़े सरणियों की तुलना में तेजी से :

console.clear();
let a = new Set([1, 2, 3, 4]);
let b = new Set([5, 4, 3, 2]);


let a_minus_b = new Set([...a].filter(x => !b.has(x)));
let b_minus_a = new Set([...b].filter(x => !a.has(x)));
let a_intersect_b = new Set([...a].filter(x => b.has(x))); 

console.log([...a_minus_b]) // {1}
console.log([...b_minus_a]) // {5}
console.log([...a_intersect_b]) // {2,3,4}


1
बड़े सरणियों के लिए इंडेक्सऑफ की तुलना में काफी तेज है।
एस्टुस फ्लास्क

100
जावास्क्रिप्ट सेट में संघ /
अंतरविरोध

6
मैं पूरी तरह से सहमत; इन्हें js इंजन में लागू किया जाने वाला निम्न स्तर का प्राइमेटिव होना चाहिए। यह मेरे से भी परे है ...
राफेल

4
@SwiftsNamesake वहाँ बिल्ट-इन तरीकों के लिए एक प्रस्ताव है, जो उम्मीद जनाउरी 2018 के बारे में बात की जाएगी github.com/tc39/agendas/blob/master/2018/01.md
जॉन

15

आप रैखिक स्कैनिंग से बचने के लिए एक नक्शे के रूप में एक वस्तु का उपयोग कर सकते Bके प्रत्येक तत्व के लिए Aके रूप में user187291 के जवाब :

function setMinus(A, B) {
    var map = {}, C = [];

    for(var i = B.length; i--; )
        map[B[i].toSource()] = null; // any other value would do

    for(var i = A.length; i--; ) {
        if(!map.hasOwnProperty(A[i].toSource()))
            C.push(A[i]);
    }

    return C;
}

गैर-मानक toSource()विधि का उपयोग अद्वितीय संपत्ति नाम प्राप्त करने के लिए किया जाता है; यदि सभी तत्वों में पहले से ही अद्वितीय स्ट्रिंग अभ्यावेदन हैं (जैसा कि अंकों के साथ होता है), तो आप toSource()इनवोकेशन को ड्रॉप करके कोड को गति दे सकते हैं ।


9

सबसे छोटा, jQuery का उपयोग कर रहा है:

var A = [1, 2, 3, 4];
var B = [1, 3, 4, 7];

var diff = $(A).not(B);

console.log(diff.toArray());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


यह अंतर की एक वस्तु देता है।
ड्रू बेकर

2
jQuery notअब 3.0.0-rc1 के रूप में सामान्य वस्तुओं के साथ काम नहीं करता है। देखें github.com/jquery/jquery/issues/3147
मार्क-एंड्रे लाफ्यून

2
यह एक ~ 70k 3 पार्टी पुस्तकालय पर निर्भरता को जोड़ने के लिए एक बहुत अच्छा विचार नहीं है बस यह करने के लिए, के रूप में यहां अन्य उत्तर में दिखाया गया है के बाद से एक ही बात कोड के कुछ ही लाइनों में पूरा किया जा सकता। हालाँकि, यदि आप पहले से ही अपने प्रोजेक्ट पर jQuery का उपयोग कर रहे हैं तो यह ठीक काम करेगा।
CBarr

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

यह केवल ए के तत्वों की राशि (2 इस मामले में) वापस कर रहा है जो बी में नहीं हैं। 2 को सरणी में बदलना व्यर्थ है ...
एलेक्स

6

मेरे पास ए बी होगा, फिर ए में मौजूद ए से मौजूद मान रखें:

function getHash(array){
  // Hash an array into a set of properties
  //
  // params:
  //   array - (array) (!nil) the array to hash
  //
  // return: (object)
  //   hash object with one property set to true for each value in the array

  var hash = {};
  for (var i=0; i<array.length; i++){
    hash[ array[i] ] = true;
  }
  return hash;
}

function getDifference(a, b){
  // compute the difference a\b
  //
  // params:
  //   a - (array) (!nil) first array as a set of values (no duplicates)
  //   b - (array) (!nil) second array as a set of values (no duplicates)
  //
  // return: (array)
  //   the set of values (no duplicates) in array a and not in b, 
  //   listed in the same order as in array a.

  var hash = getHash(b);
  var diff = [];
  for (var i=0; i<a.length; i++){
    var value = a[i];
    if ( !hash[value]){
      diff.push(value);
    }
  }
  return diff;
}

यह ठीक वैसा ही एल्गोरिथ्म है जिसे मैंने आधे घंटे पहले पोस्ट किया था
क्रिस्टोफ़

@Christoph: आप सही हैं ... मैं यह नोटिस करने में विफल रहा। मैं अपने कार्यान्वयन को समझने में अधिक सरल लगता हूं, हालांकि :)
एरिक ब्रेकेमियर

मुझे लगता है कि गेट डिफरेंस के बाहर के अंतर की गणना करना बेहतर है ताकि इसे कई बार पुन: उपयोग किया जा सके। हो सकता है कि वैकल्पिक हो: जैसे getDifference(a, b, hashOfB), यदि इसे पारित नहीं किया गया है तो गणना की जाएगी अन्यथा इसका पुन: उपयोग किया जाता है।
क्रिस्टोफ रूसो

4

क्रिस्टोफ़ से विचार को शामिल करना और सरणियों और वस्तुओं / हैश ( eachऔर दोस्तों) पर कुछ गैर-मानक पुनरावृत्ति विधियों को शामिल करना , हम कुल 20 लाइनों में रैखिक समय में अंतर, मिलन और अंतरंगता प्राप्त कर सकते हैं:

var setOPs = {
  minusAB : function (a, b) {
    var h = {};
    b.each(function (v) { h[v] = true; });
    return a.filter(function (v) { return !h.hasOwnProperty(v); });
  },
  unionAB : function (a, b) {
    var h = {}, f = function (v) { h[v] = true; };
    a.each(f);
    b.each(f);
    return myUtils.keys(h);
  },
  intersectAB : function (a, b) {
    var h = {};
    a.each(function (v) { h[v] = 1; });
    b.each(function (v) { h[v] = (h[v] || 0) + 1; });
    var fnSel = function (v, count) { return count > 1; };
    var fnVal = function (v, c) { return v; };
    return myUtils.select(h, fnSel, fnVal);
  }
};

यह मानता है कि eachऔर filterसरणियों के लिए परिभाषित किया गया है, और हमारे पास दो उपयोगिता विधियाँ हैं:

  • myUtils.keys(hash): हैश की कुंजी के साथ एक सरणी देता है

  • myUtils.select(hash, fnSelector, fnEvaluator): fnEvaluator कुंजी / मूल्य जोड़े पर कॉल करने के परिणामों के साथ एक सरणी fnSelectorदेता है जिसके लिए यह सही है।

select()शिथिल कॉमन लिस्प से प्रेरित है, और केवल है filter()और map()एक में गिर गयी है। (यह बेहतर होगा कि उन्हें परिभाषित किया जाए Object.prototype, लेकिन ऐसा करने से jQuery के साथ कहर होता है, इसलिए मैं स्थिर तरीकों से बस गया।)

प्रदर्शन: के साथ परीक्षण

var a = [], b = [];
for (var i = 100000; i--; ) {
  if (i % 2 !== 0) a.push(i);
  if (i % 3 !== 0) b.push(i);
}

50,000 और 66,666 तत्वों के साथ दो सेट देता है। इन मूल्यों के साथ एबी लगभग 75ms लेता है, जबकि संघ और चौराहे लगभग 150ms हैं। (मैक सफारी 4.0, समय के लिए जावास्क्रिप्ट तिथि का उपयोग करते हुए।)

मुझे लगता है कि कोड की 20 लाइनों के लिए सभ्य भुगतान है।


1
आपको अभी भी जांचना चाहिए hasOwnProperty()कि क्या तत्व संख्यात्मक हैं: अन्यथा, परिणाम सेट में Object.prototype[42] = true;साधन की तरह कुछ 42कभी नहीं हो सकता है
क्रिस्टोफ़

दी कि इस तरह से 42 सेट करना संभव होगा, लेकिन क्या कोई अर्ध-यथार्थवादी उपयोग मामला है जहां कोई भी वास्तव में ऐसा करेगा? लेकिन सामान्य तार के लिए मैं बिंदु लेता हूं - यह आसानी से कुछ Object.prototype चर या फ़ंक्शन के साथ संघर्ष कर सकता है।
:०२ पर jg-faustus

3

अंडरस्कोर का उपयोग करना। js (कार्यात्मक जेएस के लिए पुस्तकालय)

>>> var foo = [1,2,3]
>>> var bar = [1,2,4]
>>> _.difference(foo, bar);
[4]

3

कुछ सरल कार्य, @ मिलन के उत्तर से उधार लेना:

const setDifference = (a, b) => new Set([...a].filter(x => !b.has(x)));
const setIntersection = (a, b) => new Set([...a].filter(x => b.has(x)));
const setUnion = (a, b) => new Set([...a, ...b]);

उपयोग:

const a = new Set([1, 2]);
const b = new Set([2, 3]);

setDifference(a, b); // Set { 1 }
setIntersection(a, b); // Set { 2 }
setUnion(a, b); // Set { 1, 2, 3 }

2

उपवास के तरीके के रूप में, यह इतना सुंदर नहीं है, लेकिन मैंने यह सुनिश्चित करने के लिए कुछ परीक्षण चलाए हैं। एक सरणी को लोड करना एक वस्तु के रूप में बड़ी मात्रा में संसाधित करने के लिए बहुत तेज़ है:

var t, a, b, c, objA;

    // Fill some arrays to compare
a = Array(30000).fill(0).map(function(v,i) {
    return i.toFixed();
});
b = Array(20000).fill(0).map(function(v,i) {
    return (i*2).toFixed();
});

    // Simple indexOf inside filter
t = Date.now();
c = b.filter(function(v) { return a.indexOf(v) < 0; });
console.log('completed indexOf in %j ms with result %j length', Date.now() - t, c.length);

    // Load `a` as Object `A` first to avoid indexOf in filter
t = Date.now();
objA = {};
a.forEach(function(v) { objA[v] = true; });
c = b.filter(function(v) { return !objA[v]; });
console.log('completed Object in %j ms with result %j length', Date.now() - t, c.length);

परिणाम:

completed indexOf in 1219 ms with result 5000 length
completed Object in 8 ms with result 5000 length

हालाँकि, यह केवल स्ट्रिंग्स के साथ काम करता है । यदि आप गिने हुए सेटों की तुलना करना चाहते हैं, तो आप parseFloat के साथ परिणाम देखना चाहेंगे


1
क्या यह b.filter(function(v) { return !A[v]; });दूसरे कार्य में c = नहीं होना चाहिए ?
फेबियनमोरोनज़िरफास

तुम सही हो। किसी तरह यह मेरे लिए और भी तेज़ प्रतीत होता है
SmujMaiku

1

यह काम करता है, लेकिन मुझे लगता है कि एक और बहुत छोटा है, और सुरुचिपूर्ण भी है

A = [1, 'a', 'b', 12];
B = ['a', 3, 4, 'b'];

diff_set = {
    ar : {},
    diff : Array(),
    remove_set : function(a) { ar = a; return this; },
    remove: function (el) {
        if(ar.indexOf(el)<0) this.diff.push(el);
    }
}

A.forEach(diff_set.remove_set(B).remove,diff_set);
C = diff_set.diff;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.