0

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.

Shibbir
  • 1,963
  • 2
  • 25
  • 48
  • See [Javascript - sort array based on another array](/q/13304543/4642212) — `productKeys.sort((keyA, keyB) => preDefinedHeader.indexOf(keyA) - preDefinedHeader.indexOf(keyB));` is a start. You can try using this code along with an understanding of how [`sort`](//developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) and [`indexOf`](//developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) work. – Sebastian Simon Oct 15 '22 at 06:50
  • @SebastianSimon Thanks for that. I am using this now: `productKeys.sort(function(a, b){ return preDefinedHeader.indexOf(a) - preDefinedHeader.indexOf(b); });` But doesn't return anything. – Shibbir Oct 15 '22 at 06:56
  • How do you test the return value? The array `productKeys` itself is being modified. Have you logged it? – Sebastian Simon Oct 15 '22 at 07:07
  • @SebastianSimon https://codeshare.io/eVyVZr. Check this please. I got a new array but the order is not correct. – Shibbir Oct 15 '22 at 07:10

5 Answers5

2

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);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

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)
Nick
  • 138,499
  • 22
  • 57
  • 95
  • Yea. thats the output I am looking for :) – Shibbir Oct 15 '22 at 07:15
  • I found a issue. For e.g. I don't have `title` key in `productKeys`. Now, I can see the output inclues that `title` key but it shouldn't – Shibbir Oct 15 '22 at 07:19
  • @Shibbir that won't happen - see my edit, I've removed `title` from `productKeys` and it is not in the output – Nick Oct 15 '22 at 07:22
  • Can you tell me what is the key point I need to learn from this function? – Shibbir Oct 15 '22 at 07:28
  • 1
    `sort` functions have to return positive if the first value (`a`) is "greater than" the second (`b`), negative if the first value is "less than" the second, and 0 if they are "equal". So to sort something to the end, you should always return a value that indicates that value is "greater than" the value it is being compared to. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#description – Nick Oct 15 '22 at 07:31
0

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"]
Titan XP
  • 399
  • 3
  • 11
  • What if there are values in `preDefinedHeader` which are not in `productKeys`? – Nick Oct 15 '22 at 07:15
  • Thanks for your comment. I have amended the code to take your input into consideration. It can omit values which are not in productKeys. Thanks – Titan XP Oct 15 '22 at 07:36
  • It seems from OPs comments on my answer that they want that functionality. – Nick Oct 15 '22 at 07:37
  • Yes, you are correct. I have amended the codes. It seems working fine. Thanks – Titan XP Oct 15 '22 at 07:39
0
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.

Daniel Smith
  • 179
  • 8
-1

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)
Carsten Massmann
  • 26,510
  • 2
  • 22
  • 43