0

I have a array of makes as follows:

const makes = [
{id: "4", name: "Audi"},
{id: "5", name: "Bmw"},
{id: "6", name: "Porsche"},
{id: "31", name: "Seat"},
{id: "32", name: "Skoda"},
{id: "36", name: "Toyota"},
{id: "38", name: "Volkswagen"}
]

And I want to sort that array based on another array:

const preferred_makes = ['Volkswagen', 'Audi'];

What I do now is as follows:

const preferred_makes = ['Volkswagen', 'Audi'];

const makes = [
{id: "4", name: "Audi"},
{id: "5", name: "Bmw"},
{id: "6", name: "Porsche"},
{id: "31", name: "Seat"},
{id: "32", name: "Skoda"},
{id: "36", name: "Toyota"},
{id: "38", name: "Volkswagen"}
]

const mainMakes = []
const otherMakes = []

makes.map(make => _.includes(preferred_makes, make.name) ? mainMakes.push(make) : otherMakes.push(make))

console.log(mainMakes)
console.log(otherMakes)

But is there any better way? Can I sort makes to show those preferred_makes as first elements of the array?

Here is the fiddle.

5
  • 1
    please add the wanted result. Commented Jul 1, 2019 at 12:56
  • Your approach is bad from every point of view, did you know what's the purpose of function Array.prototype.map? Commented Jul 1, 2019 at 12:57
  • You don't need lodash. ['x'].includes('x') === true Commented Jul 1, 2019 at 12:57
  • @AluanHaddad you don't need to compare a boolean result against a boolean value. You can either negate it or simply use the result. Commented Jul 1, 2019 at 13:02
  • @Ele I was just illustrating that it returns a boolean result in what I thought was a concise way. Commented Jul 1, 2019 at 13:23

5 Answers 5

2

A regular array.sort() with a custom comparison function should be able to do this.

const preferred_makes = ['Volkswagen', 'Audi'];

const makes = [
  {id: "4", name: "Audi"},
  {id: "5", name: "Bmw"},
  {id: "6", name: "Porsche"},
  {id: "31", name: "Seat"},
  {id: "32", name: "Skoda"},
  {id: "36", name: "Toyota"},
  {id: "38", name: "Volkswagen"}
]

const sorted = makes.slice().sort((a, b) => {
  // Convert true and false to 1 and 0
  const aPreferred = new Number(preferred_makes.includes(a.name))
  const bPreferred = new Number(preferred_makes.includes(b.name))
  
  // Return 1, 0, or -1
  return bPreferred - aPreferred
})

console.log(sorted)

Sign up to request clarification or add additional context in comments.

Comments

2

You could take an object with the by one incremented indices and take a default value of Infinity for not found names. Then sort by the delta of the values.

var preferred_makes = ['Volkswagen', 'Audi'],
    preferred = preferred_makes.reduce((o, k, i) => (o[k] = i + 1, o), {});
    array = [{ id: "4", name: "Audi" }, { id: "5", name: "Bmw" }, { id: "6", name: "Porsche" }, { id: "31", name: "Seat" }, { id: "32", name: "Skoda" }, { id: "36", name: "Toyota" }, { id: "38", name: "Volkswagen" }];

array.sort((a, b) => (preferred[a.name] || Infinity) - (preferred[b.name] || Infinity));

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

Comments

1

You could use reduce to make two arrays without the requirement for sorting:

const preferred_makes = ['Volkswagen','Audi'];
const makes = [{id:"4",name:"Audi"},{id:"5",name:"Bmw"},{id:"6",name:"Porsche"},{id:"31",name:"Seat"},{id:"32",name:"Skoda"},{id:"36",name:"Toyota"},{id:"38",name:"Volkswagen"}];

const [mainMakes, otherMakes] = makes.reduce(([a, b], { id, name }) => ((preferred_makes.includes(name) ? a : b).push({ id, name }), [a, b]), [[], []]);

console.log(mainMakes);
console.log(otherMakes);
.as-console-wrapper { max-height: 100% !important; top: auto; }

To make it even faster, you could Set.prototype.has instead of includes:

const preferred_makes = new Set(['Volkswagen','Audi']);
const makes = [{id:"4",name:"Audi"},{id:"5",name:"Bmw"},{id:"6",name:"Porsche"},{id:"31",name:"Seat"},{id:"32",name:"Skoda"},{id:"36",name:"Toyota"},{id:"38",name:"Volkswagen"}];

const [mainMakes, otherMakes] = makes.reduce(([a, b], { id, name }) => ((preferred_makes.has(name) ? a : b).push({ id, name }), [a, b]), [[], []]);

console.log(mainMakes);
console.log(otherMakes);
.as-console-wrapper { max-height: 100% !important; top: auto; }

Comments

0

With lodash you can generate a dictionary of the original index by the car's make (indexByMake) using _.invert() to get an object of { [car make]: original array index }, and map the values back to numbers.

Use _.orderBy() to sort the array, and use the values from indexByMake according to the name:

const preferred_makes = ['Volkswagen', 'Audi'];
const array = [{ id: "4", name: "Audi" }, { id: "5", name: "Bmw" }, { id: "6", name: "Porsche" }, { id: "31", name: "Seat" }, { id: "32", name: "Skoda" }, { id: "36", name: "Toyota" }, { id: "38", name: "Volkswagen" }];

const indexByMake = _.mapValues(_.invert(preferred_makes), Number);

const result = _.sortBy(array, ({ name }) => indexByMake[name]);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Comments

0

You can also sort by the Array.indexOf if the index is there otherwise use String.localeCompare. No real need for lodash really:

const makes = [ {id: "4", name: "Audi"}, {id: "6", name: "Porsche"}, {id: "31", name: "Seat"}, {id: "32", name: "Skoda"}, {id: "5", name: "Bmw"}, {id: "36", name: "Toyota"}, {id: "38", name: "Volkswagen"} ] 
const list = ['Volkswagen', 'Audi'];

let result = makes.sort((a,b) => {
  let i = list.indexOf(a.name)
  return i < 0 ? a.name.localeCompare(b.name) : list.indexOf(b.name) - i
})

console.log(result)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.