سؤال

لدي شيء مثل:

{
    a : 'foo',
    b : 'bar',
    c : 'foo',
    d : 'baz',
    e : 'bar'
}

أريد تقليل التكرارات مثل:

{
    ac : 'foo',
    be : 'bar',
    d : 'baz'
}

ما هي الطريقة الجيدة للقيام بذلك؟

بعض التحذيرات:

  • لن يكون هناك سوى عدد صغير من الأزواج. (حاليًا هناك 7 ؛ يمكن أن أتخيل أن الأمر يتعلق ، على سبيل المثال ، 20.)
  • لن تكون أسماء الممتلكات الأولية شخصية واحدة فقط ، كما في المثال
  • يمكن أن تعمل القيم إلى عدة مئات من الأحرف.
  • كل من السرعة وطول الكود مهمان للغاية ، ولكن بالنظر إلى عدد قليل من الصفوف ، ربما لا يزال وضوح التعليمات البرمجية هو الأكثر أهمية.
هل كانت مفيدة؟

المحلول

اذهب من خلال كل خاصية للكائن وبناء اخر الكائن ، حيث تكون المفاتيح هي قيم الأولى ، والقيم هي قوائم المفاتيح (من الأول). ثم تعود عبر هذا الكائن الثاني وتقديم النتيجة النهائية.

شيء من هذا القبيل:

function noDupes(obj) {
  var o2 = {};
  for (var k in obj) {
    if (obj.hasOwnProperty(k)) {
      var list = o2[obj[k]] || [];
      list.push(k);
      o2[obj[k]] = list;
    }
  }
  var rv = {};
  for (k in o2) {
    if (o2.hasOwnProperty(k))
      rv[o2[k].join('')] = k;
  }
  return rv;
}

الآن ، إذا كانت قيم الكائن الأصلي ليست سلاسل ، فإن الأمور تتورط أكثر: يمكن أن تكون السلاسل فقط مفاتيح خاصية في كائن JavaScript. يمكنك أن تنظر حولها لتنفيذ تجزئة أكثر عمومية ، في هذه الحالة. إذا كانت الأشياء الخاصة بك تميل إلى أن تكون صغيرة جدًا (أقل من 10 خصائص أو نحو ذلك) يمكنك كتابة N2 الإصدار ، حيث تكرر ببساطة على الخصائص ثم تكرر مرة أخرى لكل واحد. من المحتمل أن تكون هذه فكرة سيئة ، ولكن إذا كانت الأشياء الخاصة بك قد تكون كبيرة وعليك أن تؤدي هذه العملية كثيرًا.

نصائح أخرى

var Reduce = function(obj)
{
  var temp = {};
  var val = "";

  for (var prop in obj)
  {
    val = obj[prop];
    if (temp[val])
      temp[val] = temp[val] + prop.toString();
    else
      temp[val] = prop.toString();
  }

  var temp2 = {};

  for (var prop in temp)
  {
    val = temp[prop];
    temp2[val] = prop.toString();
  }

  return temp2;
};

كما تستخدم:

var obj = {
  a :"foo",
  b : "bar",
  c : "foo",
  d : "bar", 
  e : "bar"
};

var ob2 = Reduce(obj);

هذا هو أقصر يمكنني الحصول عليه:

var obj, newObj = {}; // obj is your original
for (var i in obj) {
    if (!obj.hasOwnProperty(i)) continue;
    for (var j in newObj) {
        if (newObj.hasOwnProperty(j) && newObj[j] === obj[i]) break;
        j = "";
    }
    newObj[i + j] = obj[i];
    j && delete newObj[j];
}

تفسير:

  • يحلق من خلال كل عنصر في الكائن الأصلي ، obj, وينتج كائنًا جديدًا ، newObj.
  • لكل عنصر في الأصل ، يبحث عن نصف المنتج newObj لنفس القيمة. - النتيجه هي j, ، إما اسم الخاصية إذا تم العثور عليه ، أو سلسلة فارغة إذا لم يكن كذلك.
  • في كلتا الحالتين ، يحتاج الكائن الجديد إلى خاصية تحمل نفس الاسم مثل الخاصية الحالية في الكائن الأصلي ، بالإضافة إلى هذه القيمة j.
  • كما أنه يحذف الخاصية الموجودة في newObj إذا كان هناك واحد ، لمنع تكرارات البناء.

من المسلم به ، الإعداد j = "" داخل الحلقة غير فعالة. يمكن استبدال هذا بسهولة بمجموعة متغيرة ثانية "" في البداية ، و j فقط إذا تم العثور على مباراة. قررت أن أذهب للبساطة رغم ذلك.

بدون مكتبة ذات طابع أعلى ، فقط حلقة كل زوج (استخدم hasOwnProperty) وإضافة/إلحاق المفتاح إلى رسم بياني حيث يكون مفتاح الرسم البياني هو قيمة الزوج وقيم الرسم البياني هي المفاتيح المتسلسلة. ثم عكس مفتاح/قيم الرسم البياني.

يحرر: إذا لم تكن القيم الأولية سلاسل (ولا تعين على خريطة عكسية) ، فقد لا تزال مكتبة "هوية" الحالية تمكن النهج أعلاه من العمل.

بدلاً من ذلك ، يمكنك أن تقول ، [[k,v],...] وفرز ثم استخدم نهجًا مشابهًا لـ نوع دلو (تخيل أنه تم فرزه بالفعل) لدمج قيم "مفاتيح متساوية" في ممر الإخراج.

قد يحدث مثل هذا (في حين أن الكود قد يحتوي على أخطاء ، فإن النهج سليم - سيعمل أيضًا مع كائنات تعسفية كقيم طالما أن لديك وسيلة لمقارنة القيم):

var _f = []
for (var k in map) {
  if (map.hasOwnProperty(k)) {
    _f.push({k: k, v: map[k]})
  }
}
// you could also sort on name (a.k), if it's important
// this makes it more versatile and deterministic in output
// ordering than the histogram method above
var f = _f.sort(function (a, b) { return a.v < b.v ? 1 : a.v > b.v ? -1 : 0 })

var res = {}
var prev
var name = ""
// after the sort all {k:,v:} objects with the same values will be grouped
// together so we only need to detect the change to the next value
// and everything prior to that gets the merged key
for (var i = 0; i < f.length; i++) {
  var p = f[i]
  if (prev != p.v && name) {
    res[name] = prev
    name = ""
  } else {
    name = name + p.k
  }
  prev = p.v
}
if (name) { // don't forget last set of values
  res[name] = prev
}

// have res

سامحني إذا كنت خارجًا تمامًا ، لكن يبدو لي أن الطريقة التي تجمع بها بينها ، لديك المفاتيح والقيم في الاتجاه الخاطئ. ماذا عن هذا؟

{
    'foo': ['a', 'c'],
    'bar': ['b', 'e'],
    'baz': ['d']
}

يجب أن يكون سهلاً بما يكفي لتحويل:

flippedObj = {};
for (var letter in obj) {
    if (obj.hasOwnProperty(letter)) {
        var letters = flippedObj[obj[letter]];
        if (letters === undefined) {
            letters = [];
            flippedObj[obj[letter]] = letters;
        }

        letters.push(letter);
    }
}

(ملموسة الدماغ ؛ قد يكون هناك بعض الأخطاء.)

ابدأ بتقليل يستخدم قاموسًا لحساب العلامات بطريقة مقلوبة. طريقة أداء للغاية لأنه يستخدم دعم القاموس المدمج ، لا للحلقات وما إلى ذلك.

var flipped = Object.keys(input).reduce(function(a,b){
  var tag = input[b];
  a[tag] = (a[tag] || '') + b;
  return a;
}, {});

إرجاع كائن بتنسيق مقلوب:

// {foo: "ac", bar: "be", baz: "d"}

ثم اقلب التنسيق:

Object.keys(flipped).reduce(function(a,b){
  a[flipped[b]]=b;
  return a;
}, {});

انتاج:

// {ac: "foo", be: "bar", d: "baz"}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top