Home > Mobile >  sort array by specific attribute value and prioritize it to the top of array in JavaScript
sort array by specific attribute value and prioritize it to the top of array in JavaScript

Time:03-26

I have an array of objects, and was trying to sort the array by prioritizing a specific id value and assigning it to the top of the array. The array is being returned via an API, and can be seen as the following data:

[
    {
        "id": 310,
        "title": "Breast Cancer Screening"
    },
    {
        "id": 315,
        "title": "Depression in Adults Screening"
    },
    {
        "id": 322,
        "title": "Aspirin Use to Prevent Cardiovascular Disease"
    },
    {
        "id": 329,
        "title": "Syphilis Infection in Nonpregnant Adults and Adolescents Screening"
    },
    {
        "id": 337,
        "title": "Latent Tuberculosis Infection Screening"
    }
]

So lets say in our case, we want to prioritize "id": 329 and want to move that to the top of the array resulting in the following:

[
    {
        "id": 329,
        "title": "Syphilis Infection in Nonpregnant Adults and Adolescents Screening"
    },
    {
        "id": 310,
        "title": "Breast Cancer Screening"
    },
    {
        "id": 315,
        "title": "Depression in Adults Screening"
    },
    {
        "id": 322,
        "title": "Aspirin Use to Prevent Cardiovascular Disease"
    },
    {
        "id": 337,
        "title": "Latent Tuberculosis Infection Screening"
    }
]

I was trying to implement the following function below to accomplish this sorting and filtering, but was having difficulties getting this to work. Any help or guidance on the right approach to accomplish this would be greatly appreciated!

sorting function

const prioritizeList = (recommendationsList) => {
        const prioritizedRecommendations = recommendationsList.specificRecommendations
        console.log("initialize prioritizedRecommendations", prioritizedRecommendations)

        const prioritizeArray  = [
            {"id":329}
        ];

        const arrayFiltered = prioritizedRecommendations.filter(item => prioritizeArray.find(i => i.id === item.id))

        let newArr = prioritizeArray.concat(arrayFiltered)
}

CodePudding user response:

I would:

  • Create a set of priority ids -- so not objects with an id property, but the id values themselves.
  • Use a callback function to sort

Here is how to would look:

const prioritizeList = (recommendationsList) => {
    const prioritizedRecommendations = recommendationsList.specificRecommendations;
    const prioritizeSet = new Set([329]);

    const newArr = prioritizedRecommendations.sort((a, b) => 
        prioritizeSet.has(b.id) - prioritizeSet.has(a.id)
    );
    return newArr; // Note that the original array has been sorted in-place
}

CodePudding user response:

I will do that this way...

A) the method:

const data = 
[ { id: 310, title: 'Breast Cancer Screening' } 
, { id: 315, title: 'Depression in Adults Screening' } 
, { id: 322, title: 'Aspirin Use to Prevent Cardiovascular Disease' } 
, { id: 329
  , title: 'Syphilis Infection in Nonpregnant Adults and Adolescents Screening' 
  } 
, { id: 337, title: 'Latent Tuberculosis Infection Screening' } 
] 

data.sort(({id:a},{id:b})=>(a===329)?-1:(b===329)? 1:a-b)

console.log( data )
.as-console-wrapper {max-height: 100% !important;top: 0;}
.as-console-row::after {display: none !important;}

with a Priority list :

const data = 
  [ { id: 310, title: 'Breast Cancer Screening' } 
  , { id: 315, title: 'Depression in Adults Screening' } 
  , { id: 322, title: 'Aspirin Use to Prevent Cardiovascular Disease' } 
  , { id: 329, title: 'Syphilis Infection in Nonpregnant Adults and Adolescents Screening' } 
  , { id: 337, title: 'Latent Tuberculosis Infection Screening' } 
  ]

const SortingPrioritize = (PrioList = []) =>
  {
  return ({id:a},{id:b}) => 
    {
    let aPos = PrioList.indexOf(a)
      , bPos = PrioList.indexOf(b)
      ;
    return (aPos>-1 && bPos>-1) ? aPos - bPos 
                    : (aPos>-1) ? -1 
                    : (bPos>-1) ?  1 
                    : a-b
  } }

data.sort( SortingPrioritize([ 329, 315 ]) )

console.log( data )
.as-console-wrapper {max-height: 100% !important;top: 0; }
.as-console-row::after {display: none !important; }

CodePudding user response:

Solution

Basically, we want to have two sorting steps:

  1. Sort the IDs in ascending order.
  2. Sort the IDs by priority.

const list = [
  { "id": 329, "title": "Syphilis Infection in Nonpregnant Adults and Adolescents Screening" },
  { "id": 310, "title": "Breast Cancer Screening" },
  { "id": 315, "title": "Depression in Adults Screening" },
  { "id": 322, "title": "Aspirin Use to Prevent Cardiovascular Disease" },
  { "id": 337, "title": "Latent Tuberculosis Infection Screening" }
];

const prioritizeList = (recommendationsList) => {
  const prioritizedRecommendations = [...recommendationsList];
  const prioritizedIds = [329, 315];
  
  prioritizedRecommendations.sort((a, b) => a.id - b.id);
  prioritizedRecommendations.sort((a, b) => {
    return prioritizedIds.some(id => id === b.id) - prioritizedIds.some(id => id === a.id);
  });
  
  return prioritizedRecommendations;
}

console.log(prioritizeList(list));

Note that every following sorting takes priority over the previous. Since we want the array to be primarily ordered by priority, we sort by priority last.

Premature optimization!

Since we always want to perform two sortings, and the initial order of the array is irrelevant to the final sorting, we can collapse the two sorting functions into one.

Collapsing them into one is easily done by short-circuiting them:
X || Y, or in words, "Do X, if it was insignificant do Y".

Since we do already have our sorting functions established, and they conveniently return zero (which is falsy) if ordering is insignificant, we can simply OR them together (short-circuit them!).

Here, we are effectively saying: "Prioritized first; if both or neither are prioritized, then sort by IDs."
Writing this out really helps in making the "skip" of the short-circuit obvious!

const list = [
  { "id": 329, "title": "Syphilis Infection in Nonpregnant Adults and Adolescents Screening" },
  { "id": 310, "title": "Breast Cancer Screening" },
  { "id": 315, "title": "Depression in Adults Screening" },
  { "id": 322, "title": "Aspirin Use to Prevent Cardiovascular Disease" },
  { "id": 337, "title": "Latent Tuberculosis Infection Screening" }
];

const prioritizeList = (recommendationsList) => {
  const prioritizedRecommendations = [...recommendationsList];
  const prioritizedIds = [329, 315];
  
  prioritizedRecommendations.sort((a, b) => {
    return prioritizedIds.some(id => id === b.id) - prioritizedIds.some(id => id === a.id)
        || a.id - b.id;
  });
  
  return prioritizedRecommendations;
}

console.log(prioritizeList(list));

Note that now the first expression is prioritized instead of the other way around, since it can now skip over the subsequent expressions—again, see short-circuiting.

Sidenotes

Array.prototype.sort() does not return a new but the same array. The variable name newArr is misleading as no new array is created. Instead, I shallow-copied the array using the spread syntax (...).

To have it more functional, I made the function prioritizeList() return a copy of the list. Also, it now expects the list directly instead of accessing a field of the list which apparently is yet another list.

Ideally you'd want to pass in another array for the prioritized IDs, but here I have opted for an hard-coded array. Go do it better in your code!

  • Related