0

There is a nested array which consist of bundles and those bundles in turn have items. So, I want to skip identical item(s) of the next bundle based on previous bundle items while iterating. Below is data model and code snippets:

vm.data

[
  {
    id: '01',
    name: 'Dummy1',
    items: [{
      id: 'itemOne',
      name: 'ItemOne',
      desc: 'ItemOne description'
    }]
  },
  {
    id: '02',
    name: 'Dummy2',
    items: [{
      id: 'itemOne',
      name: 'ItemOne',
      desc: 'ItemOne description'
    },
    {
      id: 'otherItem',
      name: 'OtherItem',
      desc: 'OtherItem description'
    }]
  },
  ...
]

Html:

<div ng-repeat="bundle in vm.data track by $index">
  ...
  <ul>
    <li ng-repeat="item in bundle.items" ng-if="vm.check(item, $parent.$index)">
      <span ng-bind="item.name"></span>
      ...
    </li>
  </ul>
</div>

vm.check:

vm.check = function(item, bundleIdx) {
  if (bundleIdx > 0) {
    return _.some(vm.data[bundleIdx-1].items, function(obj) {
      return obj.id !== item.id;
    });
  } else {
    // first bundle, so show all items
    return true;
  }
};

Demo is here.

It works partially, i.e. second bundle correctly matches conditions, but third bundle not. So, what I'm missing? Any help would be appreciated!

bofanda
  • 10,386
  • 8
  • 34
  • 57
  • Why not simply build an array of unique items before consuming it using `ng-repeat`? It would simplify your template and move the complex logic where it belongs, in javascript. – Jake Holzinger Jul 10 '19 at 23:47

1 Answers1

1

I would keep the complex logic out of your template. Instead you should transform vm.data before you attempt to consume it.

var items = {};
vm.bundles = [];
vm.data.forEach(function(data) {

    var bundle = {
        id: data.id,
        name: data.name,
        items: []
    };

    data.items.forEach(function(item) {
        if (!items[item.id]) {
            bundle.items.push(item);
        }
        items[item.id] = true;
    });

    vm.bundles.push(bundle);
});

Then your template can simply consume the transformed data.

<div ng-repeat="bundle in vm.bundles track by $index">
    ...
    <ul>
        <li ng-repeat="item in bundle.items">
            <span>{{item.name}}</span>
            ...
        </li>
    </ul>
</div>
Jake Holzinger
  • 5,783
  • 2
  • 19
  • 33
  • Thanks for the answer. Your proposed solution is handy for small data structures... vm.data (data structure) which I mentioned in my question is actually quite big in terms of fields per object, so I simplified it... And using of your solution for such (huge) data would be high-chart (duplicating data fields in js side) for me. But I give you *ThumbsUp* – bofanda Jul 16 '19 at 20:33
  • If you have to do it in the HTML you could use a filter instead of an `ng-if` directive (e.g. `ng-repeat="item in bundle.items | filterItems"`), which may fit your use case better. But your `vm.check` method would still not be very performant since it iterates through all of the previous items for every item, you would likely want to optimize that in some way (like in my answer). Extremely large data sets are not DOM friendly, if you are rendering a lot of the data in the web page you will run into performance issues in either approach. – Jake Holzinger Jul 16 '19 at 20:43