Filter an array by comparing it against the elements of another array

My current task involves working with a list of objects, each object having 4 properties. To aid in filtering these objects, there is a checkbox list containing unique values from two of the properties.

The generated Filter array could appear as follows:

[
    {
        prop: 'username',
        val: ['max', 'sam']
     },
     {
        prop: 'color',
        val: ['blue', 'green']
     }
]

Here's an example of the list of objects:

[
    {
        username: 'sam',
        color: 'blue'
    },
    {
        username: 'jimmy',
        color: 'blue'
    },
    {
        username: 'sam',
        color: 'black'
    },
    {
        username: 'max',
        color: 'green'
    },
    {
        username: 'max',
        color: 'blue'
    }
]

The Desired Result is:

[
    {
        username: 'sam',
        color: 'blue'
    },
    {
        username: 'max',
        color: 'green'
    },
    {
        username: 'max',
        color: 'blue'
    }
]

I'm currently using a loop to filter through the transactions, but it feels like an endless cycle. I suspect that recursion may provide a solution. Here's my existing code:

var temporary = scope.transactions;

function getFilteredTransactions() {
    var filter = deviceFilterService.get();

    if (filter.length > 0) {
        var temp2 = [];
        angular.forEach(filter, function (fil) {
            angular.forEach(fil.val, function (filterValue) {
                angular.forEach(temporary, function (transaction) {                                 
                    if (transaction[fil.prop] === filterValue) {
                        if (temp2.indexOf(transaction) === -1) {
                            temp2.push(transaction);
                        }
                    }
                });

                temporary = temp2;
            });
        });

        $log.debug(temporary);
        scope.transactions = temporary;
    } else {
        initialize();
    }
}

While this approach works initially, it runs into issues during the second iteration for the color property. It seems to want to add the same transaction to the temp2 array. There must be a more efficient way to handle this situation, possibly involving recursion.

Answer №1

If you transform the initial list into a dictionary format, I believe it will simplify the process.

var dict = {};
angular.forEach(source1, function(item){
  dict[item.prop] = item.val;
});


function filterData(item){
  for(var prop in item){
    if(dict[prop] && dict[prop].indexOf(item[prop]) === -1){
      return false;
    }
  }
 return true;
};

You can simply execute it by:

var result = scope.transactions.filter(filterData);

Check out the Demo here


The main idea behind this code is to convert:

[
    {
        prop: 'name',
        val: ['John', 'Alice']
     },
     {
        prop: 'color',
        val: ['red', 'blue']
     }
];

into:

{ 
  name:['John', 'Alice'],
  color:['red', 'blue']
 }

This transformation greatly simplifies the lookup process.

Answer №2

To improve clarity, you may consider modifying the variable names in this code snippet. However, the following code accomplishes the task you've described:

        var uniqueValues = {};
        angular.forEach(initialData, function(data) {
            angular.forEach(data, function(val, prop) {
                if (angular.isUndefined(uniqueValues[prop])) {
                    uniqueValues[prop] = [];
                }
                if (uniqueValues[prop].indexOf(val) === -1) {
                    uniqueValues[prop].push(val);
                }
            })
        });
        var finalResult = [];
        angular.forEach(uniqueValues, function(value, key) {
            finalResult.push({property: key, uniqueValue: value})
        });

Answer №3

To filter data efficiently, iterate through each key that requires filtering, find the corresponding filter for that key, and compare the value against the filter values:

$scope.transactions = $scope.transactions.filter(isItemValidFilter);

function isItemValidFilter(item) {
    var filters = deviceFilterService.get();

    // Loop through each property in the data to retrieve the appropriate filter
    var totalConditions = Object.keys(item).length;
    var correctConditions = 0;

    for (var filterKey in item) {
        var correctFilters = filters.filter(function(dataFilter) {
            return dataFilter.prop == filterKey
        });

        if (correctFilters.length) {
            // Assuming only one filter, accessing index 0
            var correctFilter = correctFilters[0];
            var conditions = correctFilter.val;

            if (conditions && conditions.length) {
                // Check and match the values
                if (conditions.indexOf(item[filterKey]) > -1) {
                    correctConditions++;
                }
            }
        }

    }
    return correctConditions === totalConditions;
}

See a demo here: http://jsfiddle.net/Lz32hka5/1/

Answer №4

Here is a suggested solution to try out:

var temp2 = [], matched;

angular.forEach(temporary, function(item){
    matched = true;
    angular.forEach(Object.keys(item), function(key){
        angular.forEach(filter, function(filter){
            filter.prop == key && filter.val.indexOf(item[key]) == -1 && (matched = false);
        });
    });
    matched && temp2.push(item);
});

console.log(temp2)

temporary contains the list of objects, filter represents your filters

If you'd like to see a demonstration, visit: http://jsfiddle.net/wZVanG/7wnae850/

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to add a blob to the document object model (

I am a beginner when it comes to working with blobs, and I am looking for some guidance to avoid wasting hours on unsuccessful brute-force attempts. I have been using the PHP code below (sourced from here) to retrieve the base64-encoded image from my data ...

achieving bidirectional binding within a directive without using an isolated scope

One of my directives is set up like this: angular.module('somemodule').directive('tooltipText', function ($timeout, $compile, $document, config, $filter) { return { restrict: 'A', scope:{ tooltip: '=&ap ...

Utilizing @ symbol for passing arguments to $resource

Within my controller: UserResource.find({ userId: userId }, function (records) { $scope.user= records; }); In the resource I've created: angular.module("main_k"). factory("main_k.service.resou ...

Navigate audio tracks with JavaScript

I'm currently facing an issue with using a button to switch between two audio tracks being played in the browser. The method I have implemented switches to the second track, but it doesn't change the audio that is played afterward - instead, it s ...

Error occurs when incorporating OrbitalControl into Three.js leads to TypeError

Attempting to incorporate OrbitalControls into a Three.js Script to allow users to view two spheres in an empty space. However, upon running the code, the following error is encountered: three.js:5353 Uncaught TypeError: Cannot read property 'center& ...

Rotate Chevron icon downwards on mobile devices in a Multilevel Dropdown Bootstrap 4

Bootstrap 4 is being utilized for my current project, I am looking to modify the rotation of the chevron icon in a multi-level dropdown menu specifically on mobile devices when the parent link is tapped, Here is the code snippet for the multi-level dropd ...

Creating and experimenting with AngularJS applications (and applications overall)

I am currently working on developing an app, following the trend of using a REST API for the back-end and Angular (previously Backbone and jQuery) for the front-end. Initially, stubbing the REST API for testing purposes is straightforward; however, as deve ...

What is the best way to delete table rows based on their assigned class?

Within my HTML document, there is the following structure: <table id="registerTable"> <tr class="leaderRegistration"> <!--table contents--> </tr> <tr class="leaderRegistration"> <!--table conten ...

Identify the name of a specific location within a provided text

As a PHP developer, I am in the process of adding a search feature to my application that will allow users to search using keywords and location. For example: "Plumbers in York", "Electricians in Birmingham" or "Gardeners in NE63" Each search string cons ...

What is the best way to display a hidden ng-repeat item that is already hidden?

I have a list that is displayed using ng-repeat: <div class="name" ng-hide="name.hide" ng-repeat="name in nameArray" ng-click="checkName(name)"> When one of the items in the list is clicked, I want it to be hidden. In my controller, I'm handli ...

Modify content on a link using AngularJS

Currently, my setup looks like this: HTML Code <div ng-app="home" ng-controller="customersCtrl"> <div ng-bind-html="home" id="content">{{content}}</div> </div> JS Code angular.module('home', []).controller('c ...

Attempting to create a pattern of illuminated tiles on a webpage

Similar Question: Making tiles on a web page light up different colours in a sequence I am currently attempting to illuminate specific tiles within a 4x4 grid on a webpage. The grid has been set up, but the designated tiles do not light up as intende ...

Data retrieval takes precedence over data insertion

I'm facing an issue where I am trying to insert data into MySQL using Knex within a loop, but the data retrieval is happening before the insertion. Can anyone assist me with this problem? for (let i = 0; i < fileArray.length; i++) { fileLocation ...

Is using a DWR proxy with a JSON store possible in ExtJS?

I'm currently working on configuring a JsonStore to utilize a function that runs asynchronously and accepts arguments, but the process is a bit confusing for me. The function myMethod requires a callback, but how can I connect the callback data to th ...

Items plummeting below the surface plane within cannon.js and three.js

I've recently delved into the world of physics engines, specifically utilizing cannon-es.js alongside three.js. However, I've encountered a challenge where the box and loaded model are simply passing through the plane and disappearing from view. ...

Ways to ensure that the form data stays consistent and visible to the user continuously

Is there a way to ensure that the user submits the form, stores it in a MYSQL database, and then displays the same submitted data in the form field? <div class="form-group"> <label class="control-label col-sm-2" for="lname">Last Name*</l ...

Take a custom action once all elements have been rendered with ng-repeat

Looking to manipulate a DIV element generated by ng-repeat: <div ng-repeat="data in info" > <div id='plot_{{data.id}}'></div> </div> Here is the sequence of events: Information added to $scope.info ng-repeat exec ...

Learn how to utilize the value of an element attribute to toggle the visibility of a div using AngularJS

Looking to dynamically show a specific div based on the value of its attribute. <div ng-show="" data-views="{{viewstate}}" id="Question_ID_{{questionid}}" >aaa</div> Trying to achieve something similar to this: <div ng-show="data-views" ...

Can the dropbox option be automatically updated when input is entered in another text field?

I'm working on a form with a dropdown containing category names and a text field for entering the category code. What I need is for selecting a category name from the dropdown to automatically display the corresponding category code in the text field, ...

Accessing the statusText of a 403 response in Axios (React, Express, Node.js)

Within my component, there is a register function that makes an API call. The API checks if the email already exists and provides an appropriate status and statusText based on the result. Below is the API call handler in my Express app: router.post(' ...