0

I am trying to filter a Javascript dictionary based on multiple conditions.

I tried doing these steps:

  1. I created an empty dictionary variable

  2. Selected the input element and get the raw HTML node

  3. Saved the value of the element that was changed as a variable

  4. Saved the id of the element that was changed as another variable

  5. conditionally add values to the filters object

    // 1. Create a variable to keep track of all the filters as an object.
     var filters = {};
    
     // 3. Use this function to update the filters. 
     function updateFilters() {
    
     // 4a. Save the element that was changed as a variable.
    
     let changedElement = d3.select(this);
    
     // 4b. Save the value that was changed as a variable.
     let elementValue = changedElement.property("value")
     console.log(elementValue);
    
    
     // 4c. Save the id of the filter that was changed as a variable.
     let filterId = changedElement.attr("id");
     console.log(filterId);
    
    
     // 5. If a filter value was entered then add that filterId and value
     // to the filters list. Otherwise, clear that filter from the filters object.
     if (elementValue != "") {
       filters[filterId] = elementValue;
     }
     else {
       delete filters[filterId];
     }
    
     // 6. Call function to apply all filters and rebuild the table
     filterTable();
    

In the next function I need to loop through the filters list and keep data that matches the filter values.

// 7. Use this function to filter the table when data is entered.
function filterTable() {
  
  // 8. Set the filtered data to the tableData.
  let filteredData = tableData;

  // 9. Loop through all of the filters and keep any data that
  // matches the filter values


Object.entries(filters).forEach(([filterId, elementValue]) => {
  if (filters[filterId] != "") {
    filteredData = filteredData.filter(entry => entry.datetime === elementValue);

}
if (filters[filterId] != "") {
        filteredData = filteredData.filter(entry => entry.city === elementValue);

    }
if (filters[filterId] != "") {
        filteredData = filteredData.filter(entry => entry.state === elementValue);

    }
if (filters[filterId] != "") {
        filteredData = filteredData.filter(entry => entry.country === elementValue);

    }
if (filters[filterId] != "") {
        filteredData = filteredData.filter(entry => entry.shape === elementValue);

    }


};
// 10. Finally, rebuild the table using the filtered data
    buildTable(filteredData);
  }
  
  // 2. Attach an event to listen for changes to each filter
  d3.selectAll("input").on("change", updateFilters);
  
  
  // Build the table when the page loads
  buildTable(tableData);

    

The interface looks like this:

Filter Search

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!-- <meta http-equiv="X-UA-Compatible" content="IE=edge"> -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/superhero/bootstrap.min.css"> -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css">   
    <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script>
    <title>UFO Finder</title>
    <link
      rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
      integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
      crossorigin="anonymous"
    />
    <link href="https://fonts.googleapis.com/css?family=Ubuntu" rel="stylesheet">
    <link rel="stylesheet" href="static/css/style.css">
    
</head>
<body class="bg-dark">
    <div class="wrapper">
        <nav class="navbar navbar-dark bg-dark navbar-expand-lg">
            <a class="navbar-brand" href="index.html">UFO Sightings</a>
        </nav>
        <div class="jumbotron">
            <h1 class="display-4">The Truth Is Out There</h1>
        </div>
        <div class="container-fluid">
            <div class="row">
                <div class="col-md-4">
                    <h3>UFO Sightings: Fact or Fancy? <small>Ufologists Weigh In</small></h3>
                </div>
                <div class="col-md-8">
                    
                        
                        <p>Some Text</p>

                </div>
                <div class="container-fluid">
                    <div class="row">
                        <div class="col-md-3">
                            <form class="bg-dark">
                                <p>Filter Search</p>
                                <ul class="list-group bg-dark">
                                    <li class="list-group-item bg-dark">
                                        <label for="date">Enter Date</label>
                                        <input type="text" placeholder="1/10/2010" id="datetime"/>
                                    </li>
                                    
                                    <li class="list-group-item bg-dark">
                                        <label for="city">Enter a City</label>
                                        <input type="text" placeholder="roswell" id="city">
                                    </li>
                                    <li class="list-group-item bg-dark">
                                        <label for="state">Enter a State</label>
                                        <input type="text" placeholder="ca" id="state">
                                    </li>
                                    <li class="list-group-item bg-dark">
                                        <label for="Country">Enter a Country</label>
                                        <input type="text" placeholder="us" id="country">
                                    </li>
                                    <li class="list-group-item bg-dark">
                                        <label for="Shape">Enter a Shape</label>
                                        <input type="text" placeholder="crcle" id="shape">
                                    </li>
                                    <!-- <li class="list-group-item bg-dark">
                                        <button id="filter-btn" type="button" class="btn btn-dark" >Clear Table</button>
                                    </li> -->
                                </ul>
                            </form>

                        </div>
                        <div class="col-md-9">
                            <table class="table table-striped">
                                <thead>
                                    <tbody>
                                        <tr>
                                            <th>Date</th>
                                            <th>City</th>
                                            <th>State</th>
                                            <th>Country</th>
                                            <th>Shape</th>
                                            <th>Duration</th>
                                            <th>Comments</th>
                                        </tr>

                                    </tbody>
                                </thead>
                            
                            </table>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.js"></script>
    <script type="text/javascript" src="./static/js/data.js"></script>
    <script type="text/javascript" src="./static/js/ufo_starterCode.js"></script>
</body>
</html>

data.js

var data = [
  {
    datetime: "1/1/2010",
    city: "benton",
    state: "ar",
    country: "us",
    shape: "circle",
    durationMinutes: "5 mins.",
    comments: "4 bright green circles high in the sky going in circles then one bright green light at my front door."
  },
  {
    datetime: "1/1/2010",
    city: "bonita",
    state: "ca",
    country: "us",
    shape: "light",
    durationMinutes: "13 minutes",
    comments: "Three bright red lights witnessed floating stationary over San Diego New Years Day 2010"
  }
  ];

The goal is to be able to filter records in a table based on the filter search criteria entered by the user. Please note that I'm very new to Javascript. Any help is appreciated.

2
  • It would be helpful if you could provide the actual HTML too, i. e. the content that is shown in your image. With that we could then demonstrate more easily how you could solve your problem. Commented Jul 3, 2021 at 5:36
  • @CarstenMassmann HTML added Commented Jul 3, 2021 at 5:42

2 Answers 2

2

As you are new to JavaScript I would recommend that you start with the basics. And that means: try and leave out libraries and frameworks as much as possible. In the following snippet I removed your references to bootstrap, D3, datatables and jQuery as they are not really needed to solve the basic problem of filtering your table. This is also a requirement for posting code here in SO. You should always try to provide an "MCVE" (a minimum complete and viable example).

I chose a very basic concept for the filtering: Seeing that the input fields correspond directly to the first five columns of your table I applied the trick of going through the values of all theses inputs and compare them to the corresponding table values. These values are found by doing a forEach() loop over the table rows (<tr>) and then making sure that I get a "positive match" for the first five <td>s ([...tr.children].slice(0,5)) of each row. The text comparison is made between the lower-case version of each inp.value (stored in vals[I]) and the corresponding td.textContent, but only, if vals[i] is not empty (otherwise a positive match is returned, as an empty vals[i] means that there is no filtering for that column in place). So, if all five columns (.every()-loop) fulfil the "matching" condition the .style.display property for the current <tr> is set to "", otherwise to "none".

const data = [
  { datetime: "1/1/2010", city: "benton", state: "ar", country: "us", shape: "circle", durationMinutes: "5 mins.",
    comments: "4 bright green circles high in the sky going in circles then one bright green light at my front door." },
  { datetime: "1/1/2010", city: "bonita", state: "ca", country: "us", shape: "light", durationMinutes: "13 minutes",
    comments: "Three bright red lights witnessed floating stationary over San Diego New Years Day 2010" },
  { datetime: "1/13/2010", city: "Bonita", state: "ca", country: "us", shape: "square", durationMinutes: "3 minutes",
    comments: "Early in January we saw a blue square appear right out of nowhere." }];

// fill table:
const tb=document.querySelector("table tbody");
tb.innerHTML=
  data.map(r=>"<tr><td>"+Object.values(r).join("</td><td>")+"</td></td>").join("\n");
// filter table:
const inps=[...document.querySelectorAll("ul input")];
document.querySelector("ul").addEventListener("input",ev=>{
  const vals=inps.map(inp=>inp.value.toLowerCase());
  // filter table according to content of first 5 columns:
  [...tb.children].forEach(tr=>  // check for every table record:
    tr.style.display=[...tr.children].slice(0,5).every((td,i)=> 
      vals[i]==="" || td.textContent.toLowerCase().includes(vals[i])
    ) ? "" : "none" 
    // show record if vals[i]==="" or if vals[i] is part of the corresponding column contents
  )
})
.table-striped tbody tr:nth-child(odd) {background-color:#ddd}
<div class="wrapper">
  <div class="jumbotron">
    <h1 class="display-4">The Truth Is Out There</h1>
  </div>
  <div class="container-fluid">
    <div class="row">
      <div class="container-fluid">
        <div class="row">
          <div class="col-md-3">
            <form class="bg-dark">
              <p>Filter Search</p>
              <ul class="list-group bg-dark">
                <li class="list-group-item bg-dark">
                  <label for="date">Enter Date</label>
                  <input type="text" placeholder="1/10/2010" id="datetime" />
                </li>
                <li class="list-group-item bg-dark">
                  <label for="city">Enter a City</label>
                  <input type="text" placeholder="roswell" id="city">
                </li>
                <li class="list-group-item bg-dark">
                  <label for="state">Enter a State</label>
                  <input type="text" placeholder="ca" id="state">
                </li>
                <li class="list-group-item bg-dark">
                  <label for="Country">Enter a Country</label>
                  <input type="text" placeholder="us" id="country">
                </li>
                <li class="list-group-item bg-dark">
                  <label for="Shape">Enter a Shape</label>
                  <input type="text" placeholder="crcle" id="shape">
                </li>
                <li class="list-group-item bg-dark">
                  <button id="filter-btn" type="button" class="btn btn-dark">Clear Table</button>
                </li>
              </ul>
            </form>

          </div>
          <div class="col-md-9">
            <table class="table table-striped">
              <thead>              
                <tr>
                  <th>Date</th>
                  <th>City</th>
                  <th>State</th>
                  <th>Country</th>
                  <th>Shape</th>
                  <th>Duration</th>
                  <th>Comments</th>
                </tr>
              </thead>
              <tbody></tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the detailed explanation. I agree with you on keeping it basic. Please note however that this question is a small part of a larger data analytics project assignment which requires using Javascript, D3.js for this "small part". Thanks again for your time.
0

The original code was trying to filter the data object while looping through the filters; The code implementation below loops through the filters object while looping through the data object.
Since I would always have more rows than filters, I ran the .filter method once rather than per filter as I initially tried to do.

// 9. Loop through all of the filters and keep any data that matches the filter values
let filteredData = tableData.filter((obj) => {
    for(filterId in filters) {
        if(obj[filterId] !== filters[filterId]) {
          return false;
        }
    }
    return true;
  });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.