জাভাস্ক্রিপ্ট অ্যারে ব্যবহার করে কোনও সেট পার্থক্য গণনা করার দ্রুততম বা সর্বাধিক মার্জিত উপায় কী?


103

আসুন Aএবং Bদুটি সেট হতে হবে। আমি তাদের মধ্যে সেট পার্থক্য ( বা আপনার পছন্দ অনুসারে) গণনা করার জন্য খুব দ্রুত বা মার্জিত উপায়গুলি খুঁজছি । শিরোনাম অনুসারে দুটি সেটটি জাভাস্ক্রিপ্ট অ্যারে হিসাবে সঞ্চিত এবং ম্যানিপুলেটেড।A - BA \B

মন্তব্য:

  • গেকো-নির্দিষ্ট কৌশলগুলি ঠিক আছে
  • আমি নেটিভ ফাংশনগুলিতে লেগে থাকতে পছন্দ করব (তবে আমি যদি খুব দ্রুত লাইটওয়েট লাইব্রেরিতে খোলাম)
  • আমি দেখেছি, কিন্তু পরীক্ষিত হয়নি, জেএস.সেট (পূর্ববর্তী পয়েন্টটি দেখুন)

সম্পাদনা করুন: আমি সদৃশ উপাদান ধারণকারী সেট সম্পর্কে একটি মন্তব্য লক্ষ্য করেছি। যখন আমি "সেট" বলি আমি গাণিতিক সংজ্ঞাটি উল্লেখ করছি, যার অর্থ (অন্যান্য বিষয়গুলির মধ্যে) যে সেগুলিতে সদৃশ উপাদান নেই।


আপনি এই "সেট পার্থক্য" পরিভাষাটি কী ব্যবহার করছেন? এটি কি সি ++ বা কিছু থেকে?
জোশ স্টোডোলা

আপনার সেটে কি আছে? আপনি যে ধরণের টার্গেট করছেন তার উপর নির্ভর করে (উদাহরণস্বরূপ নাম্বার), একটি সেট পার্থক্য গণনা করা খুব দ্রুত এবং মার্জিত হতে পারে । যদি আপনার সেটে DOM উপাদান থাকে (বলুন), আপনি ধীর indexOfপ্রয়োগের সাথে আটকে যাচ্ছেন ।
ক্রিসেন্ট টাটকা

@ ক্রিসেন্ট: আমার সেটগুলিতে সংখ্যা রয়েছে - নির্দিষ্ট না করার জন্য দুঃখিত। @Josh: এটা গণিত (ইন মানক সেট অপারেশন en.wikipedia.org/wiki/Set_%28mathematics%29#Complements )
ম্যাট বল


1
@ ম্যাটবাল নোপ, আমি এটি দেখেছি। তবে জোশের প্রশ্নটি বৈধ এবং উত্তরহীন ছিল তাই আমি এর উত্তর দিয়েছিলাম :)
পট

উত্তর:


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: সর্বাধিক দক্ষ সমাধান নয়, তবে অবশ্যই সংক্ষিপ্ত এবং পাঠযোগ্য
ক্রিস্টোফ

10
দ্রষ্টব্য: অ্যারে.ফিল্টারটি ক্রস ব্রাউজার সমর্থিত নয় (যেমন আইই তে নয়)। @ ম্যাটকে কিছু মনে হবে না যেহেতু তিনি বলেছিলেন যে "গেকো-নির্দিষ্ট কৌশলগুলি ঠিক আছে" তবে আমি মনে করি এটি উল্লেখ করার মতো।
এরিক ব্রাচেমিয়ার

44
এটি খুব ধীর। ও (| এ | * | বি |)
glebm

1
@ এরিক ব্রাচেমিয়ার এটি এখন সমর্থিত (যেহেতু আইই 9)। অ্যারে.প্রোটোটাইপ.ফিল্টার একটি স্ট্যান্ডার্ড ইসকামাস্ক্রিপ্ট বৈশিষ্ট্য।
কোয়ান্টিন রায়

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
আমি পুরোপুরি একমত; এগুলি জেএস ইঞ্জিনে প্রয়োগ করা নিম্ন স্তরের আদিম হওয়া উচিত। এটা আমার বাইরেও ...
রাফায়েল

4
@ সুইফটসনামসেক সেট বিল্ট-ইন পদ্ধতিগুলির একটি প্রস্তাব রয়েছে যা আশাবাদী জানুয়ারি 2018 github.com/tc39/agendas/blob/master/2018/01.md তে আলোচনা করা হবে ।
জন

15

ব্যবহারকারীর 187291 এর উত্তরের মতো Bপ্রতিটি উপাদানের জন্য রৈখিক স্ক্যানিং এড়াতে আপনি মানচিত্র হিসাবে কোনও বস্তু ব্যবহার করতে পারেন :A

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 not3.0.0-rc1 হিসাবে জেনেরিক অবজেক্টের সাথে আর কাজ করে না। Github.com/jquery/jquery/issues/3147
মার্ক-আন্দ্রে লাফোর্টুন

2
এটা একটা ~ 70k 3rd পার্টি লাইব্রেরি উপর নির্ভরতা যোগ করার জন্য একটি মহান ধারণা নয় শুধু এই কাজ করতে, যেমন এখানে অন্যান্য উত্তর দেখানো যেহেতু একই জিনিস কোডের মাত্র কয়েক লাইনে সম্পন্ন করা যেতে পারে। তবে আপনি যদি ইতিমধ্যে আপনার প্রকল্পে jQuery ব্যবহার করেন তবে এটি ঠিক কাজ করবে।
সিবিআর

যদিও এই পদ্ধতির কম কোড রয়েছে তবে এটি পৃথক পৃথক অ্যালগরিদমের স্থান এবং সময় জটিলতার এবং পদ্ধতিটি সম্পাদন করতে এটি যে ডেটা স্ট্রাকচার ব্যবহার করে তার কোনও ব্যাখ্যা সরবরাহ করে না। ডেটা স্কেল আপ বা সীমিত মেমরির অনুমতি পেলে কোনও মূল্যায়ন ছাড়াই সফটওয়্যারটি ইঞ্জিনিয়ারিং করা বিকাশকারীদের পক্ষে কালো রঙের। আপনি যদি বড় ডেটা সেট সহ এ জাতীয় পদ্ধতির ব্যবহার করেন তবে উত্স কোডে আরও গবেষণা না হওয়া পর্যন্ত পারফরম্যান্স অজানা থাকতে পারে।
ডাউনহিলসকি

এটি কেবল বি এর মধ্যে থাকা উপাদানগুলির পরিমাণ (এই ক্ষেত্রে 2) ফিরিয়ে দিচ্ছে 2 কে অ্যারে রূপান্তর করা অর্থহীন ...
অ্যালেক্স

6

আমি অ্যারে বি হ্যাশ করব, তারপরে B তে উপস্থিত না থাকা অ্যারে থেকে মানগুলি রাখব:

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

ঠিক একই অ্যালগরিদমটি আমি আধ ঘন্টা আগে পোস্ট করেছি
ক্রিস্টোফ

@ ক্রিসটফ: আপনি ঠিক বলেছেন ... আমি তা খেয়াল করতে ব্যর্থ হয়েছি। যদিও আমি বুঝতে পেরেছি আমার বাস্তবায়নটিকে আরও সহজ বলে মনে হচ্ছে :)
এরিক ব্রাচেমিয়ার

আমি মনে করি getDifferences এর বাইরের পার্থক্য গণনা করা ভাল তবে এটি একাধিকবার পুনরায় ব্যবহার করা যেতে পারে। সম্ভবত এর মতো Maybe 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 গ্রহণ করে, যখন ইউনিয়ন এবং ছেদটি প্রতিটি 150 মিমি হয়। (ম্যাক সাফারি 4.0, সময়সীমার জন্য জাভাস্ক্রিপ্ট তারিখ ব্যবহার করে))

আমি মনে করি যে 20 লাইনের কোডের জন্য এটি শালীন বেতন।


1
hasOwnProperty()উপাদানগুলি সংখ্যাসূচক কিনা তা আপনার এখনও পরীক্ষা করা উচিত : অন্যথায়, ফলাফলের Object.prototype[42] = true;42
ক্রিস্টোফ

অনুমোদিত যে এইভাবে 42 সেট করা সম্ভব হবে, তবে এমন একটি অর্ধ-বাস্তবসম্মত ব্যবহারের কেস আছে যেখানে যে কেউ আসলে এটি করবে? তবে সাধারণ স্ট্রিংয়ের জন্য আমি বিষয়টি বিবেচনা করি - এটি সহজেই কিছু অবজেক্ট.প্রোটোটাইপ ভেরিয়েবল বা ফাংশনের সাথে বিরোধ করতে পারে।
jg-faustus

3

ব্যবহার Underscore.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

তবে এটি কেবল স্ট্রিং দিয়ে কাজ করে । যদি আপনি সংখ্যাযুক্ত সেটগুলির তুলনা করার পরিকল্পনা করেন তবে আপনি পার্সফ্লোয়েটের সাথে ফলাফলগুলি ম্যাপ করতে চাইবেন


1
এটি b.filter(function(v) { return !A[v]; });দ্বিতীয় ফাংশনে সি = হওয়া উচিত নয় ?
ফিবিয়ানমোরনজিরফাস

আপনি সঠিক. একরকম এটি আমার কাছে আরও দ্রুত বলে মনে হচ্ছে
স্মুজমাইকু

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;
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.