解决商店和产品最低总价的算法

时间:2017-11-01 10:52:44

标签: javascript arrays algorithm

我有一个JSON数组产品,其中包含提供它们的嵌套数组:

let arrayOfProducts =
 [
  {
    "title": "ProductA",
    "stores": [
      {
        "name": "Store1",
        "price": 15.09
      },
      {
        "name": "Store2",
        "price": 16.30,
      },
      {
        "name": "Store4",
        "price": 16.55,
      },
      .
      .
      .
      "title": "ProductB",
      "stores": [
        {
          "name": "Store1",
          "price": 8.06
        },
        {
          "name": "Store3",
          "price": 9.25,
        },
        {
          "name": "Store4",
          "price": 9.27,
          },
        .
        .
        .
    ]

我需要找到最低数量的商店(由于额外的运输限制)的组合,以最低的总价格提供所有产品。

e.g。假设该阵列有五个产品ProductA-ProductE。在它们各自的阵列中没有可以提供所有产品的单个存储。 Store2提供产品的子集,任何其他商店也是如此。

输出应该是这样的:

[
{
    "name": "store1",
    "price": total_price_for_this_store,
    "products": [ "ProductC"]
},
{
    "name": "store2",
    "price": total_price_for_this_store,
    "products": [ "ProductA", "ProductB", "ProductD"]
},
{
    "name": "store3",
    "price": total_price_for_this_store,
    "products": [ "ProductE"]
}
]

我已设法使用javascipt的forEach和过滤器函数创建预期输出,但只有在一个或多个商店拥有所有产品而不是其中一部分时才能找到解决方案。

let results = []
arrayOfProducts.forEach((product) => {
  product.stores.forEach(store => {
    let storeIndex = results.findIndex(el => { return el.name === store.name })
    if (storeIndex === -1) { // first occurence of the shop in the array of results
      results.push({
        name: store.name,
        price: store.price,
        products : [product.title]
      })
    } else {
      results[storeIndex].price += store.price
      results[storeIndex].products.push(product.title)
    }
  })
})

let allProducts = results.filter((store) => {
  return store.products.length === arrayOfProducts.length
})

allProducts.sort(function (a, b) {
  return parseFloat(a.price) - parseFloat(b.price)
})

我如何解决这个问题?我不知道如何开始。

它是否属于LP类别的算法?

1 个答案:

答案 0 :(得分:0)

我设法找到了解决方案:

const fs = require('fs')
const _ = require('lodash')

// loading the product JSON file
let product = JSON.parse(fs.readFileSync('product.json', 'utf8'))

// create a sorted array of the title products
let productTitles = product.map(el => {
  return el.title
}).sort(sortAlphabetically)

let numberOfProducts = productTitles.length

let storeCombinations = []
let stores = []

// create the array of stores
product.forEach((product) => {
  product.stores.forEach(store => {
    let price = store.price
    let productUrl = store.productUrl
    let storeIndex = stores.findIndex(el => { return el.name === store.name })
    if (storeIndex === -1) { // first occurence of the shop in the array of results
      stores.push({
        name: store.name,
        products: [{
          title: product.title,
          price: (parseFloat(price) * product.quantity).toFixed(2)
        }]
      })
    } else {
      stores[storeIndex].products.push({
        title: product.title,
        price: (parseFloat(price) * product.quantity).toFixed(2),
      })
    }
  })
})
let comboCnter = 0

// for each of the stores see if the missing product(s) can be complemented 
// with any of the following stores.
// If true then merge the two store products
stores.forEach((el, index) => {
  for (let i = index + 1; i < stores.length; i++) {
    let currentStoreProducts = el.products.map(product => product.title).sort(sortAlphabetically)
    let nextStoreProducts = stores[i].products.map(product => product.title).sort(sortAlphabetically)

    let mergedArrays = _.uniq(currentStoreProducts.concat(nextStoreProducts))

    if (mergedArrays.length === numberOfProducts) {
      let products1 = []
      let products2 = []

      let store1Price = 0
      let store2Price = 0

      productTitles.forEach(title => {
        let index1 = el.products.findIndex(x => x.title === title)
        let index2 = stores[i].products.findIndex(x => x.title === title)

        if (index1 !== -1 && index2 !== -1) {
          if (parseFloat(el.products[index1].price) < parseFloat(stores[i].products[index2].price)) {
            store1Wins()
          } else {
            store2Wins()
          }
        }

        if (index2 === -1) {
          store1Wins()
        }
        if (index1 === -1) {
          store2Wins()
        }

        function store1Wins() {
          store1Price = (parseFloat(el.products[index1].price) + parseFloat(store1Price)).toFixed(2)
          products1.push({
            title: el.products[index1].title,
            productUrl: el.products[index1].productUrl
          })
        }

        function store2Wins() {
          store2Price = (parseFloat(stores[i].products[index2].price) + parseFloat(store2Price)).toFixed(2)
          products2.push({
            title: stores[i].products[index2].title,
            productUrl: stores[i].products[index2].productUrl
          })
        }

      })

      storeCombinations.push({
        totalPrice: (parseFloat(store1Price) + parseFloat(store2Price)).toFixed(2),
        store1: {
          name: el.name,
          price: store1Price,
          products: products1
        },
        store2: {
          name: stores[i].name,
          price: store2Price,
          products: products2
        }
      })
      comboCnter++
    }
  }
})

// sort the final result ascending prices
storeCombinations.sort((a, b) => {
  return parseFloat(a.totalPrice) - parseFloat(b.totalPrice)
})

fs.writeFileSync('storeCombinations.json', JSON.stringify(storeCombinations))
console.log('comboCnter: ' + comboCnter)



function sortAlphabetically(a, b) {
  let nameA = a.toLowerCase()
  let nameB = b.toLowerCase()
  if (nameA < nameB) { return -1 }// sort string ascending

  if (nameA > nameB) { return 1 }
  return 0 // default return value (no sorting)
}

此代码找到两个商店中最便宜的组合。