app
app

Reputation: 207

Update the count of searched results and change the selection based on the number of time user searched in array of objects

I would like to add a function in which users can go to the next searched result. Thanks, @ggorlen for helping with the recursive search.

enter image description here

I have a recursive search function that gives the first value and makes them selected = true and if it is in nested array make showTree=true.

const expandPath = (nodes, targetLabel) => {
  for (const node of nodes) {
    if (node.label.includes(targetLabel)) {
      return (node.selected = true);
    } else if (expandPath(node.item, targetLabel)) {
      return (node.showTree = true);
    }
  }
};

// Output

expandPath(testData, 'ch');

//// add variable for count  example: 1 of 25

console.log(testData);

//if user click on nextrecord after search


//nextrecord(){
//logic to remove the selected true from current and add for next
//update showtree
//update recordNumber of totalValue example: 2 of 25
//}


//child3 should get selected true and remove child1 selected true and showtree
//same add showTree= true based on selected value

//if user click on previous record after search

//previousrecord(){
//logic to remove the selected true from current and add for previous
//update showtree
//update recordNumber of totalValue example: 1 of 25
//}



console.log(testData);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script>
// Test Data
const testData = [
  {
    id: 1,
    label: 'parent1',
    item: [
      {
        id: 21,
        label: 'child1',
        item: [
          {
            id: 211,
            label: 'child31',
            item: [
              {
                id: 2111,
                label: 'child2211',
                item: [{ id: 21111, label: 'child22111' }]
              }
            ]
          },
          { id: 222, label: 'child32' }
        ]
      },
      {
        id: 22,
        label: 'child2',
        item: [
          {
            id: 221,
            label: 'child421',
            item: [{ id: 2211, label: 'child2211' }]
          },
          { id: 222, label: 'child222' }
        ]
      }
    ]
  },
  {
    id: 2,
    label: 'parent2',
    item: [
      {
        id: 21,
        label: 'child2',
        item: [
          {
            id: 511,
            label: 'child51',
            item: [
              {
                id: 5111,
                label: 'child5211',
                item: [{ id: 51111, label: 'child52111' }]
              }
            ]
          },
          { id: 522, label: 'child352' }
        ]
      }
    ]
  }
];
</script>

Upvotes: 3

Views: 642

Answers (2)

Amit Desai
Amit Desai

Reputation: 424

You can use refer following code

    const testData = [
  {
    id: 1,
    label: 'parent1',
    item: [
      {
        id: 21,
        label: 'child1',
        item: [
          {
            id: 211,
            label: 'child31',
            item: [
              {
                id: 2111,
                label: 'child2211',
                item: [{ id: 21111, label: 'child22111' }]
              }
            ]
          },
          { id: 222, label: 'child32' }
        ]
      },
      {
        id: 22,
        label: 'child2',
        item: [
          {
            id: 221,
            label: 'child421',
            item: [{ id: 2211, label: 'child2211' }]
          },
          { id: 222, label: 'child222' }
        ]
      }
    ]
  },
  {
    id: 2,
    label: 'parent2',
    item: [
      {
        id: 21,
        label: 'child2',
        item: [
          {
            id: 511,
            label: 'child51',
            item: [
              {
                id: 5111,
                label: 'child5211',
                item: [{ id: 51111, label: 'child52111' }]
              }
            ]
          },
          { id: 522, label: 'child352' }
        ]
      }
    ]
  }
];

// flatten down tree to array and add parent pointer
const flatten = (data) => {
  let flattenData = [data]
  if (data.item) {
    for (const item of data.item) {
      item.parent = data;
      flattenData = flattenData.concat(flatten(item));
    }
  }
  return flattenData;
}

let flattenData = [];

// flatten down the test data
for (const data of testData) {
  flattenData = flattenData.concat(flatten(data));
}

// to update showTree flag
const toggle = (item, expand = true) => {
  const parent = item.parent;
  if (parent) {
    parent.showTree = expand;
    if (parent.parent) {
      return toggle(parent, expand);
    }
    return parent;
  }
  return item;
}

/**
 * 
 * @param {targetLabel} query 
 * @returns function navigate with param forward flag
 */
const seach = (query) => {
  let index = -1;
  const items = flattenData.filter(x => x.label.includes(query));
  return (forward = true) => {
    if (index > -1) {
      items[index].selected = false;
      toggle(items[index], false);
    }
    index = index + (forward ? 1 : -1);
    let item = null;
    if (index > -1 && index < items.length) {
      items[index].selected = true;
      item = toggle(items[index], true);
    }
    return {
      item,
      index,
      length: items.length
    }
  }
}

const navigate = seach('child5211');

// next result
let result = navigate();

// previous result
result = navigate(false);

// result will look like this
/**
 * {
 *  item: root of current item with showTree and selected falgs or null if out of bound,
 *  index: current match,
 *  length: total match
 * }
 * 
 */

Upvotes: 1

mik rus
mik rus

Reputation: 139

Tackling one thing at a time here, you can pretty quickly get the desired 'next' functionality you want by converting you're recursive search to a generator function:

function* expandPath(nodes, targetLabel) {
  for (const node of nodes) {
    if (node.label.includes(targetLabel)) {
      yield (node.selected = true);
    } else if (expandPath(node.item, targetLabel)) {
      yield (node.showTree = true);
    }
  }
};

const gen = expandPath(mynodes, "thisTargetLabel");
gen.next()
gen.next() //<-- the next one

It's a little hard to know without more of your context how to answer the other questions, but what it really seems like you need here is state, and (es6) class is a good way to make this:

class Searcher {
  constructor(mynodes, mylabel){
    this.count=0;
    this.nodes=mynodes;
    this.label=mylabel;
    this.generateMatches(this.nodes);
    this.selectNode(this.matches[0]); // select the first node
  }

  generateMatches(nodes){
      this.matches=[];
    for (const node of nodes) {
      if (node.label.includes(this.label)) {
        this.matches.push(node);
      } else {
        this.generateMatches(nodes.node) 
      }
    }
  }

  updateTreeById(id, node){
    this.nodes.forEach(n=>n.showTree = false);
    for (const node of this.nodes) {
      if (node.id === id) {
        //noop but we are here
      } else if(this.updateTreeById(id, this.nodes.node)) {
        node.showTree = true;
      }
    }
  }

  selectNode(i){
    const index =  i % this.matches.length;
    this.currNodeId =  this.matches[index].id;
    this.matches[index].selected = true // we are wrapping around
    this.count = i; // setting your current count
    this.updateTreeById(this.matches[index].id)
    // update logic, reset trees
  }

  nextNode(){
    this.selectNode(this.count + 1)
  }
  prevNode(){
    this.selectNode(this.count - 1)
  }
}


Upvotes: 0

Related Questions