Steps for filtering multiple arrays simultaneously

Currently, I am working with two arrays that share a mutual ID, allowing me to connect them. However, I am facing a challenge with filtering.

In the first array, there is a list of items with checkboxes next to each item. When I select a checkbox, I want to filter and display corresponding items from the second array in a table by matching the mutual ID.

The issue arises when I perform multiple selections. I expect to see multiple results, but due to using the .filter method, I only get one result (the last selected checkbox).

If I select checkboxes for Item 1 and Item 2, I anticipate seeing 3 results (ids 10, 20(x2)) from the 'apps' array in the lower table. And if I deselect a checkbox, the corresponding results should be removed from the table.

 
let items = [
    {id:1, name:'Item 1', appId:10},
    {id:2, name:'Item 2', appId:20},
    {id:3, name:'Item 3', appId:20},
    {id:4, name:'Item 4', appId:30}
]
  
let apps = [
    {id:10, address:'Some street 1', city:'City 1'},
    {id:20, address:'Some street 2', city:'City 2'},
    {id:20, address:'Some street 2', city:'City 2'},
    {id:30, address:'Some street 3', city:'City 3'}
]

this.dataSource = this.items.filter(x => x.appId == apps.id)

https://i.sstatic.net/4OpRR.png

Thank you.

UPDATE

With Rohit's solution, I have successfully achieved my initial goal. However, a new challenge has emerged - selecting a checkbox in the first array clears all previously selected checkboxes in the second array. Any suggestions on how to address this issue?

I have included before and after images. After selecting the checkbox for Item 1, I expect the checkboxes for Some street 9 and Some street 10 to remain selected.

Here is my HTML code:


<table mat-table [dataSource]="dataSourceObjects" class="mat-elevation-z8">
  <ng-container matColumnDef="objectChBox">
    <th mat-header-cell *matHeaderCellDef></th>
    <td mat-cell *matCellDef="let element">
      <mat-checkbox
        class=""
        [checked]="element.chbxActive"
        (change)="checkSingleObject($event, element)"
      >
      </mat-checkbox>
    </td>
  </ng-container>

  <ng-container matColumnDef="address">
    <th mat-header-cell *matHeaderCellDef>Address</th>
    <td mat-cell *matCellDef="let element">{{element.address}}</td>
  </ng-container>

  <ng-container matColumnDef="city">
    <th mat-header-cell *matHeaderCellDef>City</th>
    <td mat-cell *matCellDef="let element">{{element.city}}</td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumnsObjects"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumnsObjects;"></tr>
</table>

https://i.sstatic.net/2SOeX.png https://i.sstatic.net/gKLXW.png

Answer №1

After reviewing your current set-up, it appears that the `apps` variable is an array. In order to access its items, you need to use the index and cannot directly access them using dot notation.

Since users can select multiple items, we should check for the `id` in the `apps` array based on the `appId` selected from the `items` array using checkboxes.

For demonstration purposes, I am assigning the `appId`s 10 and 20 directly to an array, and then fetching the corresponding results from the `apps` array using the Array#filter method along with the Array#includes method.

// Apps array
let apps = [
  {id:10, address:'Some street 1', city:'City 1'},
  {id:20, address:'Some street 2', city:'City 2'},
  {id:20, address:'Some street 2', city:'City 2'},
  {id:30, address:'Some street 3', city:'City 3'}
];

// Selected items array.
const selctedItems = [10, 20];

const result = apps.filter(({ id }) => selctedItems.includes(id));

// result
console.log(result);

Update : Detailed working demo as requested by the author (Comments have been added within the code snippet below).

// Input Array 1
let items = [
  {id:1, name:'Item 1', appId:10},
  {id:2, name:'Item 2', appId:20},
  {id:3, name:'Item 3', appId:20},
  {id:4, name:'Item 4', appId:30}
];

// Input Array 2
const apps = [
  {id:10, address:'Some street 1', city:'City 1'},
  {id:20, address:'Some street 2', city:'City 2'},
  {id:30, address:'Some street 3', city:'City 3'}
];

// Element where checkboxes will be bound. 
var myDiv = document.getElementById("checkBoxes");

// Dynamically create checkboxes for each object in "items" array and bind them to the "myDiv" element representing "checkBoxes".
for (var i = 0; i < items.length; i++) {
  var checkBox = document.createElement("input");
  var label = document.createElement("label");
  checkBox.type = "checkbox";
  checkBox.value = items[i].appId;
  myDiv.appendChild(checkBox);
  myDiv.appendChild(label);
  label.appendChild(document.createTextNode(items[i].name));
}

// Obtain all checkbox elements for iteration.
var cbs = document.querySelectorAll('input[type="checkbox"]');

// Store selected checkbox values in this variable.
let selectedItems = [];

// Iterate over each checkbox element and trigger an "onchange" event.
cbs.forEach(function(item) {
  item.onchange = getCheckBoxValue;
});

// Method to retrieve all selected checkbox values into an array.
function getCheckBoxValue() {
  selectedItems = [];
  cbs.forEach(function(item) {
    if (item.checked) {
      if (!selectedItems.includes(item.value)) {
        selectedItems.push(item.value)
      }
    }
  });
  getFilteredValuesFromAnotherArr();
}

// Final method used to fetch the selected values from another array based on the selections made.
function getFilteredValuesFromAnotherArr() {
  console.log(apps.filter(({ id }) => selectedItems.includes(id.toString())));
}
<div id="checkBoxes"></div>

Answer №2

To simplify your code and make it more readable, I suggest merging the two arrays together. By creating a dictionary for the app array using the reduce function, you can easily access app items corresponding to ids in the items array without having to loop through the entire array.

Once you have the dictionary set up, you can merge the arrays by mapping the items array and combining each item with its respective app item from the appDict.

This approach reduces complexity and maintains O(n) time complexity for solving the problem efficiently. Here's an example of how you can achieve this:

const items = [
  {id:1, name:'Item 1', appId:10},
  {id:2, name:'Item 2', appId:20},
  {id:3, name:'Item 3', appId:20},
  {id:4, name:'Item 4', appId:30}
]
const apps = [
  {id:10, address:'Some street 1', city:'City 1'},
  {id:20, address:'Some street 2', city:'City 2'},
  {id:30, address:'Some street 3', city:'City 3'}
];
const appDict = apps.reduce((acc, {id, ...rest})=> ({...acc, [id]: rest}), {})
const combinedArr = items.map(item=>({...item, ...appDict[item.appId]})

Answer №3

To effectively manage multiple items, here's a straightforward illustration to consider. Let's say App ID 10 and 30 are chosen. You'll need to update the app array to include both the original data and the selected-item outcomes as shown below:

let items = [
  { id: 1, name: "Item 1", appId: 10 },
  { id: 2, name: "Item 2", appId: 20 },
  { id: 3, name: "Item 3", appId: 20 },
  { id: 4, name: "Item 4", appId: 30 },
];

let apps = [
  { id: 10, address: "Some street 1", city: "City 1" },
  { id: 20, address: "Some street 2", city: "City 2" },
  { id: 20, address: "Some street 2", city: "City 2" },
  { id: 30, address: "Some street 3", city: "City 3" },
];

const selectedAppIDs = [10, 30];

const result = apps
  .filter((app) => selectedAppIDs.includes(app.id))
  .map((app) => ({
    ...app,
    selectedItems: items.filter((x) => x.appId === app.id),
  }));

console.log(JSON.stringify(result));

Outcomes:

[{"id":10,"address":"Some street 1","city":"City 1","selectedItems":[{"id":1,"name":"Item 1","appId":10}]},{"id":30,"address":"Some street 3","city":"City 3","selectedItems":[{"id":4,"name":"Item 4","appId":30}]}]

Experience this example

Answer №4

[UPDATE]

This piece of code is tailored to meet your specific requirements:

let items = [
    {id: 1, name: 'Item 1', appId: 10},
    {id: 2, name: 'Item 2', appId: 20},
    {id: 3, name: 'Item 3', appId: 20},
    {id: 4, name: 'Item 4', appId: 30}
]

let apps = [
    {id: 10, address: 'Some street 1', city: 'City 1'},
    {id: 20, address: 'Some street 2', city: 'City 2'},
    {id: 20, address: 'Some street 2', city: 'City 2'},
    {id: 30, address: 'Some street 3', city: 'City 3'}
]

this.dataSource = items.filter(x => apps.findIndex(y => y.id === x.appId) >= 0);

Explanation

We apply the filter function on the items array, which iterates through each element in that array and evaluates whether the provided function returns true or false for that particular element. The elements that pass this evaluation are added to a new array, which is then returned.

The function we supply for this evaluation,

x => apps.findIndex(y => y.id === x.appId) >= 0
, utilizes another built-in utility function called findIndex, which operates similar to filter but only returns one item along with its position in the array. We pass in the function y => y.id === x.appId to check if the current item in the apps array matches the one we're looking for. If no match is found, it returns -1, which we use as our comparison condition.

I trust this explanation clarifies things for you~!!

Answer №5

The approach of using .filter(...includes...).map(...) involves nested linear operations, leading to a quadratic time complexity. Similarly, utilizing .filter(...findIndex...) presents the same issue.

Below is a concise example showcasing an optimized solution incorporating:

  • The efficient utilization of Set for swift O(1) lookups
  • Integration of useMemo to prevent unnecessary computations
  • Utilization of flatMap for merging filter->map in a single iteration

function Application({ objects, applications }) {
  const [chosenItems, setChosenItems] = React.useState(_ => new Set())
  const selectedApps = React.useMemo(
    () => new Set(Array.from(chosenItems, s => s.appId)),
    [chosenItems]
  )
  const selectItem = object => event => {
    const chosen = new Set(chosenItems)
    if (chosen.has(object)) chosen.delete(object)
    else chosen.add(object)
    setChosenItems(chosen)
  }
  return <div>
    {objects.map((object, key) =>
      <div>
        <input
          key={key}
          type="checkbox"
          checked={chosenItems.has(object)}
          onClick={selectItem(object)}
        />
        {object.name}
      </div>
    )}
    {applications.flatMap((app, key) =>
      selectedApps.has(app.id)
        ? [<pre key={key}>{JSON.stringify(app)}</pre>]
        : []
    )}
  </div>
}

const objects = [{id:1, name:'Object 1', appId:10},{id:2, name:'Object 2', appId:20},{id:3, name:'Object 3', appId:20},{id:4, name:'Object 4', appId:30}]
const applications = [{id:10, address:'Some street 1', city:'City 1'},{id:20, address:'Some street 2', city:'City 2'},{id:20, address:'Some street 2', city:'City 2'},{id:30, address:'Some street 3', city:'City 3'}]

ReactDOM.render(<Application objects={objects} applications={applications} />, document.body)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>

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

The JSONP script encountered an error due to an unexpected colon

I am looking to fetch some game information from Steam using an API, however when I try to use it, I encounter the following error: No 'Access-Control-Allow-Origin' header is present on the requested resource. Additionally, when trying to use J ...

Populating JQuery autocomplete using a PHP array

As a beginner in the world of JavaScript and JQuery, I have been scouring the internet for hours trying to find a solution to my query. My goal is to populate a JQuery autocomplete feature using an array that I have created in PHP. Here is a glimpse of the ...

Encountering a "Multiple registrations of the method 'get' on the path '../api/' error" with Swagger

I am utilizing OpenAPI / Swagger in an Angular application built on .NET Core, and I am facing the error "The method 'get' on path '../api/' is registered multiple times" while generating frontend TypeScript code using NSwag Studio. Fro ...

What are some ways to dynamically find and import JavaScript files while the code is running?

Currently, I am developing a node.js-based server that manages various devices. The server informs connected clients about its capabilities by utilizing separate JS files that define objects with inheritance through util.inherits(). The issue I am facing ...

The range input in HTML does not respond when attempting to adjust it

I am having an issue where the thumb on the slider does not move when I try to. This problem occurred after I inserted an img element inside the slider-cntnr div. View the code at http://codepen.io/danielyaa5/pen/xVEXvB HTML <div id="slider-cntnr"&g ...

Displaying an alert message when incorrect login credentials are detected in React Native

My objective is to display an alert message when a user login attempt fails, however, the alert does not appear. Below is the code I am using in React Native: login onPressLogin(){ fetch('http://192.168.1.10:3000/users/login',{ m ...

Accordion's second child element experiencing issues with grid properties

I have set the parent element display:"Grid" and specified the gridColumnStart for my child elements as shown below. It seems to be working correctly for the first child element, but not for the second one. Please find my code attached: return ( ...

Can JSON be parsed using JavaScript?

Is it feasible to utilize JavaScript to parse information from an external URL hosting a JSON file on a different domain? The JSON data sample below shows various URLs with associated "q" values that I am interested in extracting. [{"url":"http://websit ...

Switching between different months in the React-Calendar display

I've been working with React-Calendar and I'm facing an issue where the month doesn't change when clicking the navigation arrows in the calendar. Here's a snippet of my code: ... const onActiveStartDateChangeHandler = ({ activeStartDa ...

AngularJS - Finding the position of an element with the query "is located at the bottom of the page"

How can we effectively determine if there is more content to be scrolled in Angular, especially when dealing with a single-page app with a fixed bottom navbar? I am looking for a way to visually signal to users that there is additional content available b ...

What are some ways to display alternative content when JavaScript is not enabled?

When JavaScript is disabled, I want to display an entirely different set of content. While I am aware that the <noscript> tag can be used for this purpose, how can I ensure that the remaining page elements are hidden as well? Any suggestions would b ...

Trouble with @Input() in Angular 2 when selector is surrounded by HTML tag

In my Angular2 project, I have a component named TabTitleComponent. import { Component, OnInit,Input,AfterViewInit } from '@angular/core'; @Component({ selector: 'tab-title', templateUrl: './tab-title.component.html', ...

Jquery double-click Event Not Functioning Properly

I've been attempting to toggle the visibility of my footer navbar while also changing the chevron icon. When the navbar is hidden, I want the chevron pointing up to be displayed, and when the navbar is shown, I want the chevron pointing down to be dis ...

The datatable does not adjust to changes in page size

My jsfiddle example showcases different card boxes, and I am currently focusing on making the 'Table 1' box responsive. However, I have encountered an issue: Check out my jsfiddle example here code If you open the jsfiddle with a large enough ...

What is the best way to move the ball within a ruler chart that spans from -100 to 100 while staying within the confines of the canvas?

Looking at a graph with measurements from -100 to 0 and 0 to 100, I am facing a challenge in adjusting the ball's position calculation based on the gradientDataChart input. This input can have a value between -100 and 100, but the issue lies in propor ...

How should the client object be properly initialized? Error: VoximplantApiClient cannot be used as a constructor

How should I properly instantiate the client object? I keep getting this error when trying to do so: const client = new VoximplantApiClient(); TypeError: VoximplantApiClient is not a constructor Is there an example of ES6 imports code available? The docu ...

Issue with React application failing to transmit files to PHP server using Ajax

I recently set up a react app on my Apache server and decided to use the react-images-upload npm package for handling user images in conjunction with Axios to post to my PHP server. However, I encountered an issue where the $_FILES array in the PHP respons ...

The calculator is malfunctioning and unable to perform calculations

export default function App() { const [activeKey, setActiveKey] = useState(0); const [storeArr, setStoreArr] = useState([]); function click(selector) { setActiveKey(selector); if (activeKey !== "=") { setStoreArr([...storeArr ...

Encountered a Uncaught TypeError: Attempted to clear a canvas when the property 'getContext' is undefined or null

After researching multiple error solutions, none seemed to work for me. It became frustrating, leading me to consider spamming just to bypass the word limit and share my own findings with others facing similar issues. Check out the Answers section below to ...

When it comes to JavaScript, the evaluation order of arguments and delayed evaluation play a crucial

Assuming function( arg1, arg2 ), is it true that arg1 will always evaluate before arg2 (unlike traditional C)? Is there a way to create a function where arguments are not evaluated immediately, but only on demand? For instance, in if( cond1 || cond2 ), c ...