Home > database >  JavaScript - How to sort array based on other array order
JavaScript - How to sort array based on other array order

Time:10-16

I have this array:

let productKeys = [ "aff_link", "availability", "brand", "date_add", "date_upd", "description", "image_link", "link", "price", "product_type", "title" ];

And another array called preDefinedHeader:

let preDefinedHeader = ['image_link', 'title', 'price', 'brand', 'link'];

Now, I want to sort the productKeys array based on the preDefinedHeader array key order only if any preDefinedHeader key exist in productKeys array.

so the output should be now;

['image_link', 'title', 'price', 'brand', 'link', 'aff_link', 'availability', 'description', 'date_add', 'date_upd', 'product_type'];

I know how to sort normally is something like:

productKeys.sort() 

But can not get any idea how to sort like I want. Please help me.

CodePudding user response:

You can use the code in the suggested link as a start, but you need to take account of the fact that some of the values in productKeys are not in preDefinedHeader. When that occurs, you want to sort those values to the end of the list, so you need to return a positive number if a is not found (productKeys.indexOf(a) == -1) and a negative number if b is not found, or a.localeCompare(b) if both are not found. Otherwise you sort based on the indexOf values. This functionality can be implemented as below:

let productKeys = ["aff_link", "availability", "brand", "date_add", "date_upd", "description", "image_link", "link", "price", "product_type"];

let preDefinedHeader = ['image_link', 'title', 'price', 'brand', 'link'];

productKeys.sort((a, b) => {
  let ia = preDefinedHeader.indexOf(a)
  let ib = preDefinedHeader.indexOf(b)
  if (ia == -1) {
    if (ib == -1) return a.localeCompare(b)
    return 1
  }
  else if (ib == -1) {
    return -1
  }
  return ia - ib
});

console.log(productKeys)

CodePudding user response:

You could sort by the index or with a large value to keep the other items at the same place.

const
    getOrder = s => preDefinedHeader.indexOf(s)   1 || Number.MAX_VALUE,
    productKeys = [ "aff_link", "availability", "brand", "date_add", "date_upd", "description", "image_link", "link", "price", "product_type", "title" ],
    preDefinedHeader = ['image_link', 'title', 'price', 'brand', 'link'];

productKeys.sort((a, b) => getOrder(a) - getOrder(b));
console.log(productKeys);

CodePudding user response:

let productKeys = [ "aff_link", "availability", "brand", "date_add", "date_upd", "description", "image_link", "link", "price", "product_type", "title" ];

let preDefinedHeader = ['image_link', 'title', 'price', 'brand', 'link'];

const productKeySet = new Set(productKeys);
const output = []

for (const item of preDefinedHeader) {
    if(productKeySet.has(item)) {
        output.push(item)
        productKeySet.delete(item)
    }
}
let arrayWithProperOrder = [...output,...productKeySet]

You could do something like this. Not sure if its the most efficient way though.

CodePudding user response:

I use filter(), includes(), sort(), concat(). You can omit the sort() if no need to sort the array after filter().

let productKeys = [ "aff_link", "availability", "brand", "date_add", "date_upd", "description", "image_link", "link", "price", "product_type", "title" ];

let preDefinedHeader = ['image_link', 'title', 'price', 'brand', 'link', 'test'];

let newPreDefinedHeader = preDefinedHeader.filter(e => productKeys.includes(e));
console.log(newPreDefinedHeader);
let filteredArr = productKeys.filter(e => !newPreDefinedHeader.includes(e));
let arr = newPreDefinedHeader.concat(filteredArr.sort((a, b) => a - b));

console.log(arr);
// ["image_link", "title", "price", "brand", "link", "aff_link", "availability", "date_add", "date_upd", "description", "product_type"]

CodePudding user response:

Here is my take on it:

let productKeys = [ "aff_link", "availability", "brand", "date_add", "date_upd", "description", "image_link", "link", "prices", "product_type", "brand", "title" ];
let ord = ['image_link', 'title', 'price', 'brand', 'link'];

// val returns a numeric string ("000"..."004") if contained in ord, otherwise the original string
const val=e=>{let i=ord.indexOf(e); return i>-1?(1000 i).toFixed(0).slice(1):e; }
console.log("comparison values:",productKeys.map(e=>`${e} => ${val(e)}`)); // shows the comparison values for productKeys

const res=productKeys.sort((a,b)=>val(a).localeCompare(val(b)));
console.log("sorted array:",res)

I modified the sample data to demonstrate further edge cases:

  • price is now prices (therefore not all of the predefinedHeader values are now available in the producKeys array
  • brand occurs twice (and will be seen twice in the resulting array too)

My approach will fail, when there are other numeric strings in the productKeys array, as they will be treated in the same way as the "special" values in ord.

In comparison, @Nick's approach works more reliable - here his version again, using ternary operators:

  let productKeys = [ "aff_link", "availability", "brand", "date_add", "date_upd", "description", "image_link", "link", "prices", "product_type", "brand", "title" ];
  let ord = ['image_link', 'title', 'price', 'brand', 'link'];

const res=productKeys.sort((a, b) => { // Nick's approach (see above) with ternary operator:
  const ia = ord.indexOf(a), ib = ord.indexOf(b);
  return ia==-1 
    ? ib==-1 
       ? a.localeCompare(b) 
       : 1 
    : ib==-1 
       ? -1 
       : ia - ib
});
console.log(res)

  • Related