2

I have an object such as:

var dataObj = {
'hre': {groupID: 1},
'bla': {groupID: 1},
3: {groupID: 0},
4: {groupID: 2},
16: {groupID: 2},
6: {groupID: 2},
7: {groupID: 0},
64893: {groupID: 0},
9: {groupID: 1}
}

When the group is 0, it means that the field doesn't pertain to any group.

Fields of the same group must be adjacent. Else it should throw an error.

So for instance the object above would throw an error because fields of group 1 are not adjacent.

Looking for an efficient way of getting this done (or any way for that matter, because I haven't found one after some time).

var error = false;
var previousGroupID = null;
var thisGroupID = null;
var previousFieldGroupID = null;

for (index in dataArray) {
    if (dataObj[index]['groupID'] > 0) {
        thisGroupID = dataObj[index]['groupID'];
        if (previousFieldGroupID == 0 && thisGroupID ==  previousGroupID) {
            error = true;
        }
    }
    previousFieldGroupID = dataObj[index]['groupID'];
}

Fiddle http://jsfiddle.net/zq2w7w9t/1/

jSmith
  • 275
  • 1
  • 4
  • 13
  • 3
    This is not possible, since properties in JS objects are not guaranteed to have any specific order. – Teemu Aug 28 '15 at 21:34
  • considering your object keys are numbers, perhaps you mean it to be an array? – ffflabs Aug 28 '15 at 21:37
  • My fields are always from 1 to 9. We can loop and be assured that the order is correct. – jSmith Aug 28 '15 at 21:37
  • We can loop, but we can't be sure the order is correct. – Teemu Aug 28 '15 at 21:38
  • @Teemu How come? Every time I loop, I see the index going from 1 to 9. – jSmith Aug 28 '15 at 21:39
  • @amenadiel I gave this object as example to make it simpler, but sometimes the keys are strings and the index is actually given as a property along other properties. – jSmith Aug 28 '15 at 21:40
  • That depends on the implementation, please check [standard](http://es5.github.io/#x12.6.4). "The mechanics and order of enumerating the properties (...) is not specified" – Teemu Aug 28 '15 at 21:42
  • Can we achieve something with what I got here? – jSmith Aug 28 '15 at 21:43
  • Using arrays as amenadiel has suggested. – Teemu Aug 28 '15 at 21:44
  • My keys aren't always numbers – jSmith Aug 28 '15 at 21:47
  • It doesn't matter, you can use an array of objects, like `[{1: {groupID: 1}}, {2: {groupID: 2}} ...]`. – Teemu Aug 28 '15 at 21:49
  • Fields in an object have no order - they are not adjacent or non-adjacent, they simply exist (or not). You need some kind of ordered list of objects, and in javascript that's probably most easily accomplished with an array. – Stephen P Aug 28 '15 at 22:22
  • Shall the algorithm return error for this? { 1: {groupID: 1}, 2: {groupID: 0}, 3: {groupID: 1} }, so does the item with 0 groupID break the group with ID 1? – Tamas Hegedus Aug 28 '15 at 22:23
  • Yes -- groups should be adjacent in any case – jSmith Aug 28 '15 at 23:21

2 Answers2

0

Assuming your JavaScript engine retains ordering of members (most of them does, but they don't have to, see this question):

function hasError(dataObj) {
  var error = false;
  var previousGroupID = -1;
  var thisGroupID = -1;
  var seenGroups = new Set();

  for (index in dataArray) {
    thisGroupID = dataObj[index].groupID;
    if (previousGroupID !== thisGroupID) {
      if (seenGroups.has(thisGroupID)) {
        if (thisGroupID !== 0) error = true;
      } else {
        seenGroups.add(thisGroupID);
      }
    }
    previousGroupID = thisGroupID;
  }
  return error;
}

Otherwise, you can use Map, which is guaranteed to retain entry ordering:

function hasError(dataMap) {
  var error = false;
  var previousGroupID = -1;
  var thisGroupID = -1;
  var seenGroups = new Set();
  var thisGroup = null;
  for (thisGroup of dataMap.values()) {
    thisGroupID = thisGroup.groupID;
    if (previousGroupID !== thisGroupID) {
      if (seenGroups.has(thisGroupID)) {
        if (thisGroupID !== 0) error = true;
      } else {
        seenGroups.add(thisGroupID);
      }
    }
    previousGroupID = thisGroupID;
  }
  return error;
}

var m = new Map();
m.set('hre', { groupID: 1 });
m.set('bla', { groupID: 1 });
m.set(3, { groupID: 0 });
m.set(4, { groupID: 2 });
m.set(5, { groupID: 0 });
m.set(1, { groupID: 1 });
console.log(hasError(m));

Or you can break it down into separate functions:

function listHasError(list) {
  var error = false;
  var previousGroupID = -1;
  var thisGroupID = -1;
  var seenGroups = new Set();
  var thisGroup = null;
  list.forEach(function(thisGroupID) {
    if (previousGroupID !== thisGroupID) {
      if (seenGroups.has(thisGroupID)) {
        if (thisGroupID !== 0) error = true;
      } else {
        seenGroups.add(thisGroupID);
      }
    }
    previousGroupID = thisGroupID;
  });
  return error;
}
function extractGroupsFromMap(dataMap) {
  var res = [];
  for (thisGroup of dataMap.values()) {
    res.push(thisGroup.groupID);
  }
  return res;
}
function extractGroupsFromObj(dataObj) {
  var res = [];
  for (index in dataObj) {
    res.push(dataObj[index].groupID);
  }
  return res;
}

var dataObj = {
  'hre': {groupID: 1},
  'bla': {groupID: 1},
  3: {groupID: 0},
  4: {groupID: 2},
  16: {groupID: 2},
  6: {groupID: 2},
  7: {groupID: 0},
  64893: {groupID: 0},
  9: {groupID: 1}
};
console.log(listHasError(extractGroupsFromObj(dataObj)));
Community
  • 1
  • 1
Tamas Hegedus
  • 28,755
  • 12
  • 63
  • 97
0

Ok, assuming that:

  • Your object has arbitrary keys
  • Each value has a numerical index attribute and a groupId
  • having non consecutive elements with group 0 isn't an error
  • index attributes are exclusive and consecutive

Then let's say your object looks like:

var    yourObject = {
    'a1': {     index: 1,       groupId: 1  },
    'b2': {     index: 2,       groupId: 1  },
    'c3': {     index: 3,       groupId: 0  },
    'd4': {     index: 4,       groupId: 5  },
    'e5': {     index: 5,       groupId: 5  },
    'g6': {     index: 6,       groupId: 5  },
    'h7': {     index: 7,       groupId: 'A'},
    'i8': {     index: 8,       groupId: 0  },
    'j9': {     index: 9,       groupId: 'A'}
    };

(please note I intentionally used non consecutive nor exclusively numerical group ids).

Then I would group your object using:

var groups={};
Object.keys(yourObject)
    .forEach(function (key) {
        var thevalue = yourObject[key],
            groupId = thevalue.groupId;

        groups[groupId] = groups[groupId] || [];

        groups[groupId].push(thevalue.index);
});

at this point, groups is an object whose keys are 0,1,5 and 'A', and their values are the value of the index atribute (which we said was consecutive and exclusive).

{
    0: [3,8],
    1: [1,2],
    5: [4,5,6],
    'A':[7,9]
}

I will iterate on groups, and for every group I'll skip the verification if it has a single element or it's group zero.

If it has more than 1 element, and it isn't group zero, then I'll sort its elements and iterate on them. Since indexes are consecutive, each of these sorted arrays must not have items with difference > 1 among them

var hasError = false;

Object.keys(groups).forEach(function (groupId) {
    if (groupId !== 0 && groups[groupId] && groups[groupId].length > 1) {

        var sortedElements = groups[groupId].sort(function (a, b) {
            return a > b;
        });
        console.log('Group ' + groupId, sortedElements);

        for (var index = 1; index < sortedElements.length; index++) {
            if ((sortedElements[index] - sortedElements[index - 1]) > 1) {
                hasError = true;
                console.warn('Error detected');
            }
        }
    }
});
ffflabs
  • 17,166
  • 5
  • 51
  • 77