Arrange an array of objects by two characteristics, with the priority given to one over the other

I have a collection of objects that look like this:

// items: [
   {type: 'FRUIT', description: 'Apple'},
   {type: 'VEGETABLE', description: 'Carrot'},
   {type: 'FRUIT', description: 'Banana'},
   {type: 'GRAIN', description: 'Rice'},
   {type: 'VEGETABLE', description: 'Broccoli'},
]

Currently, I'm arranging these objects using the following function:

const items = items.sort((a, b) => {
            if (a.type > b.type) return -1;
            if (a.type < b.type) return 1;
            return a.description.localeCompare(b.description);
          });

The resulting order is as follows:

// items: [
   {type: 'VEGETABLE', description: 'Broccoli'},
   {type: 'VEGETABLE', description: 'Carrot'},
   {type: 'FRUIT', description: 'Apple'},
   {type: 'FRUIT', description: 'Banana'},
   {type: 'GRAIN', description: 'Rice'},
]

Now, I want to prioritize one specific type, let's say FRUIT, to appear first. Therefore, the desired output should be:

// items: [
   {type: 'FRUIT', description: 'Apple'},
   {type: 'FRUIT', description: 'Banana'},
   {type: 'VEGETABLE', description: 'Broccoli'},
   {type: 'VEGETABLE', description: 'Carrot'},
   {type: 'GRAIN', description: 'Rice'},
]

I've attempted various solutions found in similar queries without success. Any recommendations or ideas? Is achieving this result feasible?

Appreciate your assistance.

[UPDATED]: After incorporating @Andrew Parks' suggestion, here's my finalized solution:

items.sort((a, b) =>
    +(b.type === "FRUIT") -
    +(a.type === "FRUIT") ||
    a.type.localeCompare(b.type) ||
    a.description.localeCompare(b.description)
);

Answer №1

A clever solution can be found here:

const dishes = [
   {category: 'LEGUME', description: 'A'},
   {category: 'VEGETABLE', description: 'B'},
   {category: 'FISH', description: 'C'},
   {category: 'LEGUME', description: 'E'},
   {category: 'LEGUME', description: 'B'},
   {category: 'LEGUME', description: 'N'},
];

const cmp = (a,b,c) => a[c].localeCompare(b[c]);
const cmpEq = (a,b,c,d) => (b[c]===d) - (a[c]===d);
dishes.sort((a,b) => 
  cmpEq(a,b, 'category', 'VEGETABLE') || 
  cmp(a,b, 'category') || 
  cmp(a,b, 'description')
);

console.log(dishes);

To reverse the order of any sorting, simply add a minus sign in front of the cmp().

Answer №2

When it comes to sorting, combining category and description can yield unique results:

const items = [
   {category: 'FRUIT', description: 'A'},
   {category: 'VEGETABLE', description: 'B'},
   {category: 'VEGETABLE', description: 'A'},
   {category: 'VEGETABLE', description: 'N'},
   {category: 'MEAT', description: 'C'},
   {category: 'FRUIT', description: 'E'},
   {category: 'FRUIT', description: 'B'},
   {category: 'FRUIT', description: 'N'}
]

items.sort((a, b) => {
  if (a.category === 'VEGETABLE') return -1
  if (b.category === 'VEGETABLE') return 1
  return `${a.category}${a.description}`.localeCompare(`${b.category}${b.description}`);
});

console.log(items)
.as-console-wrapper { max-height:100% !important; top 0; }

Answer №3

To handle the VEGETABLE value differently, you can customize your sort function:

const dishes = [
   {category: 'LEGUME', description: 'A'},
   {category: 'VEGETABLE', description: 'B'},
   {category: 'VEGETABLE', description: 'A'},
   {category: 'VEGETABLE', description: 'N'},
   {category: 'FISH', description: 'C'},
   {category: 'LEGUME', description: 'E'},
   {category: 'LEGUME', description: 'B'},
   {category: 'LEGUME', description: 'N'}
]

dishes.sort((a, b) => {
  res = (b.category == 'VEGETABLE') - (a.category == 'VEGETABLE')
  if (res) return res
  res = a.category.localeCompare(b.category)
  if (res) return res
  return a.description.localeCompare(b.description);
});

console.log(dishes)
.as-console-wrapper { max-height:100% !important; top 0; }

It's important to note that sort modifies the array directly so there is no need to store the sorted result in a new variable.

Answer №4

Begin by sorting based on whether the items belong to the VEGETABLE category or not - continue with the sorting process if this initial comparison yields a tie.

const dishes = [
   {category: 'LEGUME', description: 'A'},
   {category: 'VEGETABLE', description: 'B'},
   {category: 'VEGETABLE', description: 'A'},
   {category: 'VEGETABLE', description: 'N'},
   {category: 'FISH', description: 'C'},
   {category: 'LEGUME', description: 'E'},
   {category: 'LEGUME', description: 'B'},
   {category: 'LEGUME', description: 'N'},
];
dishes.sort((a, b) => {
  const vegResult = a.category === 'VEGETABLE' - b.category === 'VEGETABLE';
  if (vegResult) return vegResult;
  if (a.category > b.category) return -1;
  if (a.category < b.category) return 1;
  return a.description.localeCompare(b.description);
});
console.log(dishes);

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

Instructions on how to assign a function to a dynamically created button in Angular 2

I recently created a table that can generate a new row when the addRow() function is called. However, I am facing an issue where the button in the generated row is not able to call the myFunc() function. How can I resolve this problem? addRow(){ let t ...

Padding among the series items within the plot of the Apex Charts donut chart

I am trying to replicate a donut chart in apex charts that is similar to the one displayed on the right. While I have made significant progress, I am still in need of adding stroke or padding around the series items on the donut chart plot, as shown in the ...

What could be causing the Material-UI Appbar onLeftIconButtonTouchTap function to malfunction?

I am currently learning React-Redux and Material-UI. I'm working on creating a Sample App, but I'm facing some difficulties. I need help in improving my code. Specifically, I am trying to make the Drawer open when the Material-UI AppBar's on ...

Utilizing the power of the numpy library, one can effectively work

I am encountering an issue when trying to create a record array using the datetime64 type. My environment consists of Python 2.7 and Numpy 1.7. Below is a simplified example: p_dtype = np.dtype({"names": ['trns_id', 'trns_date', &apos ...

Is it possible to loop through a subset of a collection using *ngFor?

Is it possible to iterate through a specific range of elements in a collection using *ngFor? For instance, I have a group of checkboxes with their form control name and label specified as follows: [{id: 'c1', label: 'C1'}, ...] Assum ...

Detecting the rectangular boundary of rotated elements on a dynamically generated canvas or SVG using JavaScript (TypeScript)

I've been struggling with a particular issue for some time now. Currently, I'm developing a web application using TypeScript, React, and Google Maps. For the markers on the map, I aim to use custom images generated at runtime based on various pa ...

I'm looking to learn how to efficiently write file chunks from a video upload in Node Js. How can I

My current project involves attempting to stream webcam or audio data to Node.js and save it on disk. The aim is to send the chunks of data to the server as soon as they are available. I have successfully captured the stream using getUserMedia, set up me ...

Encountering issues with HMR after relocating files to the /app directory

Greetings, I am currently in the process of learning how to utilize express with webpacks HMR feature. However, every time I make changes and save a file, I encounter the following error: "The following modules couldn't be hot updated: (Full reload n ...

Modify the background color when hovering using jquery

In order to have buttons with variable colors and a different hover color, I have to apply inline CSS. Since the hover color cannot be added using inline CSS, I need to use JavaScript. I attempted to achieve this using the .hover() function, but the colors ...

Using Python, Scrapy, and Selenium to extract dynamically generated content from websites utilizing JavaScript

I am currently utilizing Python in combination with Selenium and Firefox to extract specific content from a website. The structure of the website's HTML is as follows: <html> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8"> ...

Adjusting the navigation image as it passes through various div elements during scrolling

Is it possible to dynamically change an image in the navigation bar based on the user's scroll position? For example, I want pic1 to be displayed when the page content is at the top, then switch to pic2 once the user reaches the footer, and then back ...

The elimination function in JavaScript

I've been browsing the Mozilla Developers website, focusing on the concept of the delete operator. In the final section about "Deleting array elements," there are two similar scripts presented, with the only difference being how they modify the array. ...

Tips for retrieving a specific property from an array

Using AngularJS, I am trying to fetch the 'name' property from an array using console.log. However, I am facing issues accessing this property in the array and need to be able to access it. angular.module('ob3App.lead') .controller ...

What sets apart the definition of isolated scope variables for an Angular directive when done within {} as opposed to in

When working with Angular directives, I am aware that I can assign an isolated scope by adding '=' or '@' or '&' in the {} to define variables. However, it seems like this is not required within the link function. For example: ...

ngx-datatable Retrieve the most recent clicked row using multi-click mode

Currently, I am attempting to retrieve the most recent 'clicked' row from ngx-datatable. Here is what I have in my code: <ngx-datatable [rows]="rows" [selected]="selected" [selectionType]="'multiClick'" (select)='on ...

Is it possible to use null and Infinity interchangeably in JavaScript?

I've declared a default options object with a max set to Infinity: let RANGE_DEFAULT_OPTIONS: any = { min: 0, max: Infinity }; console.log(RANGE_DEFAULT_OPTIONS); // {min: 0, max: null} Surprisingly, when the RANGE_DEFAULT_OPTIONS object is logged, i ...

Real-time updates to HTML5 progress bar with each button click

Looking to update the HTML5 progress bar value when a number is entered in a text area and button is clicked. I will only input properly formatted integer numbers into the text area. Check out the HTML code below: <textarea id="txt" cols=20 rows=1 pla ...

Utilizing the Django object array in AngularJS – a comprehensive guide

I have a Django variable structured like this: [<Topic object>, <Topic object>] When passing it to Angular using ng-init, I wrote the following: <div class="profile-navigation" ng-init="ProfileTopics={{ProfileTopics|safe}} "> However, ...

Partially extended texture in Three.js

Currently, I am using the Collada loader in Three.js r65 to load my 3D object. Upon loading, I apply a texture to all parts of the model using the following code snippet. var loader = new THREE.ColladaLoader(); loader.options.convertUpAxis = true; loader ...

Angular - Set value only if property is present

Check if the 'rowData' property exists and assign a value. Can we approach it like this? if(this.tableObj.hasOwnProperty('rowData')) { this.tableObj.rowData = this.defVal.rowData; } I encountered an error when attempting this, specif ...