Sunday, May 28, 2017

How to check for duplicate value in recursive array structure on textbox blur event?

Leave a Comment

I am trying have unique title to my node in recursive tree.

So when i give title to my nodes it should check that this title is already taken by some other nodes or not.If taken then it should alert user and it should reset that node value to previous value.

No 2 nodes should have same title.

But here as structure is recursive so i am not getting how do i do this.

Note : I want to do this as soon as textbox loses its focus.

Can anybody please help me with this??

var app = angular.module("myApp", []);          app.controller("TreeController", function ($scope) {              $scope.delete = function (data) {                  data.nodes = [];              };              $scope.add = function (data) {                  var post = data.nodes.length + 1;                  var newName = data.name + '-' + post;                  data.nodes.push({ name: newName, nodes: [],selected : false, myObj: { name: newName} });              };              $scope.tree = [{ name: "Node", nodes: [], selected: false }];                $scope.setActive = function ($event, data) {              	$event.stopPropagation();                  $scope.selectedData = data;                  clearDivSelection($scope.tree);                  data.selected = true;              };                function clearDivSelection(items) {                  items.forEach(function (item) {                      item.selected = false;                      if (item.nodes) {                          clearDivSelection(item.nodes);                      }                  });              }                            $scope.checkDuplicateNodeName = function () {               alert()              }          });
ul {      list-style: circle;  }  li {      margin-left: 20px;  }   .active { background-color: #ccffcc;}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>      <ul ng-app="myApp" ng-controller="TreeController">          <li ng-repeat="data in tree" ng-include="'tree_item_renderer.html'"></li>          <script type="text/ng-template" id="tree_item_renderer.html">              <div ng-class="{'active': data.selected}" > {{data.myObj.name}}</div>              <button ng-click="add(data)">Add node</button>              <button ng-click="delete(data)" ng-show="data.nodes.length > 0">Delete nodes</button>              <ul>                  <li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'" ng-click="setActive($event, data)"></li>              </ul>          </script>          <div style="margin-left:100px;">             Title :  <input type="text" ng-model="selectedData.myObj.name" ng-blur="checkDuplicateNodeName()" />             Location :  <input type="text" ng-model="selectedData.myObj.location" />            </div>      </ul>

2 Answers

Answers 1

You could use a recursive approach similar to your clearDivSelection method:

function isDuplicated (node, title) {   var result = false;    if (node.nodes && node.nodes.length > 0) result = node.nodes.reduce(function (result, node) {     return isDuplicated(node, title);   }, false);    return result && node.name === title; } 

Or (at a cost of memory), you could maintain a list of titles:

$scope.titles = {};  $scope.add = function (data) {   var post = data.nodes.length + 1;   var newName = data.name + '-' + post;    if ($scope.titles[newName]) return; // refuse to add   $scope.titles[newName] = true;    data.nodes.push({     name: newName,     nodes: [],     selected : false,     myObj: {       name: newName     }   }); }; 

I'm not sure exactly what you mean by

should reset that node value to previous value.

if you are "add"ing new objects, you won't have a "previous value" - but I'll leave that bit up to you. This should get you started, anyway.

Answers 2

my solution

  1. store reference to parent on every node
  2. store validated copy of name on blur (meaning there are 3 names but I dont know the point of myObj.name so I left it as is, and you can clean up)
  3. find dupes recursively, stop on first true. if there's a dupe, use last valid name, else update last valid name.

whys

  1. there can be multiple trees ?? dupe should only matter in the tree node is under. if this isnt the case you can easily modify checkDuplicateNodeName to use the root node
  2. you want to validate the name only on blur. angular provides ways to validate ng-model on every change via, parsers and formatters which would completely restrict the user from persisting a dupe name. there are a ton of ways to handle this problem (validators, editing without persisting) and all require different kinds of solutions. explore and go with what works best for you
  3. using a hashMap (my first thought) would require cleanup logic on renames and deletes that would've ended up complicated the code further

var app = angular.module("myApp", []);  app.controller("TreeController", function($scope) {    $scope.delete = deleteNodes;    $scope.add = add;    $scope.setActive = setActive;    $scope.checkDuplicateNodeName = checkDuplicateNodeName;    $scope.trees = [{      name: "Node",      nodes: [],      selected: false    }];      function deleteNodes(data) {      data.nodes = [];    }      function add(data) {      var post = data.nodes.length + 1;      var newName = data.name + '-' + post;            data.nodes.push({        name: newName,        nodes: [],        selected: false,        myObj: {          name: newName        },        parent: data,        validatedName: newName      });    }      function setActive($event, data) {      $event.stopPropagation();      if($scope.selectedData) {        $scope.selectedData.selected = false;      }      $scope.selectedData = data;            data.selected = true;    }      function checkDuplicateNodeName() {      if(!$scope.selectedData)        return;          var name = $scope.selectedData.myObj.name;      var tree = getTree($scope.selectedData);      var dupe = isDuplicateName(tree, name);      if(dupe){        alert('The name "' + name + ' already exists');        $scope.selectedData.myObj.name = $scope.selectedData.validatedName;      } else {        $scope.selectedData.validatedName = name;      }      }        function getTree(node){      while(node.parent) {        node = node.parent;      }            return node;    }        function isDuplicateName(node, name) {      var dupeName = node.name && name && node.name == name;      if(dupeName) return true;            if(node.nodes){        for(var idx=0; idx< node.nodes.length; idx++) {          if(isDuplicateName(node.nodes[idx], name)){            return true;          }        }      }      return false;    }  });
ul {    list-style: circle;  }    li {    margin-left: 20px;  }    .active {    background-color: #ccffcc;  }
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>      <ul ng-app="myApp" ng-controller="TreeController">    <li ng-repeat="data in trees" ng-include="'tree_item_renderer.html'"></li>    <script type="text/ng-template" id="tree_item_renderer.html">      <div ng-class="{'active': data.selected}"> {{data.myObj.name}}</div>      <button ng-click="add(data)">Add node</button>      <button ng-click="delete(data)" ng-show="data.nodes.length > 0">Delete nodes</button>      <ul>        <li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'" ng-click="setActive($event, data)"></li>      </ul>    </script>    <div style="margin-left:100px;">      Title : <input type="text" ng-model="selectedData.myObj.name" ng-blur="checkDuplicateNodeName()" /> Location : <input type="text" ng-model="selectedData.myObj.location" />      </div>  </ul>

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment