Home > Back-end >  Sorting based on the category level
Sorting based on the category level

Time:10-31

Just started diving into the array of objects sorting in JavaScript, having come up with the following snippet. Is there a chance this code can somehow be optimised for performance or does this look generally legit?

const products = [
  {
    title: "Test product1",
    description: "Test description",
    price: 5,
    category: {
      title: 'hot_dishes',
      priority: 2
    },
    ru: {
        title: "Тестовый продукт",
        description: "пошел на хуй"
    }
  },
  {
    title: "Test product2",
    description: "Test description",
    price: 5,
    category: {
      title: 'dessert',
      priority: 1
    },
    ru: {
        title: "Тестовый продукт",
        description: "пошел на хуй"
    }
  }
];

const sorted = products
.map(({ category }) => category)
.sort((a, b) => parseFloat(a.priority) - parseFloat(b.priority))
.map(({ title }) => (
  products.filter(({ category: { title: cTitle } }) => title === cTitle)
));

console.log(sorted);
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Workflow:

  1. Destructure category from each product
  2. Sort the in ascending order
  3. Filter in separate arrays, based on the category title

My current biggest concern is about the following line:

.map(({ title }) => (
  products.filter(({ category: { title: cTitle } }) => title === cTitle)
));

CodePudding user response:

You are indirectly sorting the array using priority, But you don't have to go through all the steps. You can just use sort on the products also

const sorted = products.sort((a, b) => a.category.priority - b.category.priority);

const products = [
    {
        title: "Test product1",
        description: "Test description",
        price: 5,
        category: {
            title: "hot_dishes",
            priority: 2
        },
        ru: {
            title: "Тестовый продукт",
            description: "пошел на хуй"
        }
    }, {
        title: "Test product2",
        description: "Test description",
        price: 5,
        category: {
            title: "dessert",
            priority: 1
        },
        ru: {
            title: "Тестовый продукт",
            description: "пошел на хуй"
        }
    }
];

const sorted = products.sort((a, b) => a.category.priority - b.category.priority);

console.log(sorted);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

You can optimize this further, and there are some edge cases to watch out for based on the shape of your input.

On thing you could do overall is to normalize categories into a separate collection and reference the category id in the product. This would remove the possibility of having the same category title with different priorities in different objects.

You also haven't handled the sorting behavior of multiple categories with the same priority in a way that guarantees sort order, you should sort by priority and alpha by title to fix that. This sort call relies on the browser's implementation of sort() and therefore should be well optimized.

Your algorithm maps over product and filters product for each iteration of the map, which is O(n^2) time complexity. Making this a O(n) algorithm will save you a ton of time on a big dataset.

I've added a snippet that de-duplicates the categories using a Map before sorting, and caches the category array index before grouping using a Map there too. Those optimizations are admittedly pretty small compared to the overall improvement of finding a single pass algorithm for grouping your products.

An empty 2 dimensional array is created from the deduped and sorted category array. Then we iterate over the products array and add the products to the proper category array.

We've made the sort a little faster and more accurate; everything else is now accomplished in amortized constant time.

const products = [{
    title: "Test product1",
    description: "Test description",
    price: 5,
    category: {
      title: 'hot_dishes',
      priority: 2
    },
    ru: {
      title: "Тестовый продукт",
      description: "пошел на хуй"
    }
  },
  {
    title: "Test product2",
    description: "Test description",
    price: 5,
    category: {
      title: 'dessert',
      priority: 1
    },
    ru: {
      title: "Тестовый продукт",
      description: "пошел на хуй"
    }
  }
];

const categoryComparator = (a, b) => {
  const priorityComparison = parseFloat(a.priority) - parseFloat(b.priority)
  if ( priorityComparison !== 0 ) return priorityComparison
  return a.title.localeCompare(b)
}

const categoryMap = new Map()
products.forEach(product =>
  categoryMap.set(product.category.title, product.category)
)

const sortedCategories = Array.from(categoryMap, ([title, category]) => category)
  .sort(categoryComparator)
  .map(category => category.title)

const categoryIndexMap = new Map([...new Set(sortedCategories)].map((category, index) => [category, index]))

const categorizedProductArrays = Array.from({
  length: categoryIndexMap.size
}, i => [])

products.forEach((product) =>
  categorizedProductArrays[categoryIndexMap.get(product.category.title)].push(product))

console.log(categorizedProductArrays)
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related