匹配数组中的对象并进行合并

更新:

我有一个名为cars的对象数组,其中包含带有关于汽车属性数据的li标签(例如价格,汽车类型等)。 我的目标是根据某些标准将这些汽车整合到一个单一的列表中。

要求

  1. 快速表现
  2. 保持相同的汽车arrays结构
  3. 主要目标:匹配预付费和零售清单 – 将零售清单中的HTML(例如按钮和定价信息)合并到预付清单中。 看到: 在此处输入图像描述
  4. 如果匹配(基于IF语句中的标准),则删除匹配的列表,而不使用“listing-prepaid”类,并使用零售列表中的某些信息更新匹配的预付费列表。

汽车arrays:

 
    • 5
    • 2

    Compact

    Nissan Versa or similarCar on Airport
    Unlimited Miles
    Pick-up
    SFB: Orlando Sanford Intl Airport
    Drop-off
    Same as pick-up
    Pay Now & Save 2%
    $25$24/day

    Pay Now

    Total: $34
    • 4
    • 1

    Economy

    Chevrolet Spark or similar
    Unlimited Miles
    Pick-up
    3575 Vineland Road, Orlando, FL
    Drop-off
    Same as pick-up
    Pay Now & Save 9%
    $33$29/day

    Pay Now

    Total: $36
    • 5
    • 2

    Compact

    Nissan Versa or similarCar on Airport
    Free CancellationPay at Pick-upUnlimited Miles
    Pick-up
    SFB: Orlando Sanford Intl Airport
    Drop-off
    Same as pick-up
    $25/day

    Select Car

    Total: $35
    • 2
    • 1

    Economy

    SmartCar or similarShuttle to Car
    Pay at Pick-upUnlimited Miles
    Pick-up
    MCO: Orlando Intl Airport
    Drop-off
    Same as pick-up
    $14/day

    Select Car

    Total: $28
  • 预期产出:

    在上面的示例数组中,第一个和第三个列表应该是匹配的(因为它们具有相同的车型,位置ID,车辆示例等)。 应该从数组中删除第一个列表,因为它没有类别列表预付费,并​​且.column-price中的HTML应该添加到其预付费匹配中(在此示例中,数组中的第3个列表)。

    最终产品:

    在此处输入图像描述

    码:

      cars = cars.reduce((acc, car) => { let retail_match = false; cars.forEach(car2 => { if (((car[0].hasAttribute("data-original-price") && car[0].getAttribute("data-original-price") === car2[0].getAttribute("data-price")) || (car2[0].hasAttribute("data-original-price") && car2[0].getAttribute("data-original-price") === car[0].getAttribute("data-price"))) && (car[0].getAttribute("data-base-price") != car2[0].getAttribute("data-base-price")) && (car[0].getAttribute("data-price") != car2[0].getAttribute("data-price")) && (car[0].getAttribute("data-type") == car2[0].getAttribute("data-type")) && (car[0].getAttribute("data-vehicle-example") == car2[0].getAttribute("data-vehicle-example")) && (car[0].getAttribute("data-location-id") == car2[0].getAttribute("data-location-id")) && (car[0].getAttribute("data-dropoff-location-id") == car2[0].getAttribute("data-dropoff-location-id"))) { if (!car.hasClass("listing-prepaid")) retail_match = true; else { car.find(".column-price") .addClass("prepaid-match") .append(car2.find(".column-price div.retail")) .find("div.retail:not(.prepaid) p.button a").text("Pay Later"); } } }); if (!retail_match) acc.push(car); return acc; }, []); 

    正如评论中所提到的,使用reduce可以将复杂度保持在O(n) 。 这基本上意味着,两倍大小的列表将花费两倍的时间,因为算法仅迭代汽车列表一次。

    如果你需要将cars数组中的每个项目与cars数组中的每个其他项目进行比较,那么类似循环的方法的复杂性将达到O(n ^ 2) ,对于每个附加项目(粗略地说),将会有指数级更多循环/时间使用。

    我不是100%肯定你的javascript对象的数据结构,但以下方法应该工作:

     const allCars = []; // An array of cars, each item is a HTMLElement let matchedCars = allCars.reduce((acc, car, cars) => { cars.forEach(car2 => { // For every car iterate over the cars array again to compare car to every item in the cars array (leave out this loop if you don't need the extensive comparison) if (car.hasAttribute("data-original-price") && car2.getAttribute("data-original-price") === car.getAttribute("data-price") /* Add additional matching criteria here, you may access cars to get info about other cars than the current car */) { // Add the desired class for a match car.classList.add('listing-prepaid'); // Add the matched car to the accumulator, so it ends up in the matchedCars array acc.push(car); } }); }, []; 

    另一种方法是构建一个数据结构,允许在恒定时间内( O(1) )根据属性访问元素。 一个例子是(哈希)地图 。 在这种情况下,对于算法循环的每个元素,不需要再次循环遍历整个列表以识别匹配,而是查询Map结构的匹配。

    额外:鉴于汽车是HTMLElement,您可以使用dataset属性更轻松地访问data- *值:

     car.dataset.originalPrice === car.dataset.price 

    有关详细信息, 请访问https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes

    一般来源: https : //developer.mozilla.org/en-US/docs/Web/API/HTMLElement,https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array /降低

    我完全不同地处理这个问题。 为了帮助您入门,以下解决方案应该让您走上正确的道路。 根据提供的数据集,它还应满足您的所有 (或大多数)要求。

     const carsUniq = new Map() cars.forEach($car => { const cKeys = $car.data() const carAttrsId = [ cKeys.dropoffLocationId, cKeys.locationId, cKeys.type, cKeys.vehicleExample ].join('') const sCar = carsUniq.get(carAttrsId) if (!sCar) { carsUniq.set( carAttrsId, cKeys ) } else { for(const c in sCar) { if ( !sCar[c] && cKeys[c] ) sCar[c] = cKeys[c] } } }) 

    它是如何工作的?

    1. 创建用于跟踪汽车的地图。
    2. 通过将特定的汽车道具引用到carAttrsId来确定汽车是否重复。 [O(1)查找]
    3. 如果在Map中找到汽车,它必须是重复的,因此我们将数据集合并为一个标准化对象。
    4. 最终结果是carsUniq.values()是一个类似于独特汽车对象的数组。

    基于您的数据集carsUniq包含3个独特的汽车

     "SX-34.0910834--118.352194SX-34.0910834--118.352194ICARChevrolet Cruze" => {…} "ZR-34.1958--118.3489ZR-34.1958--118.3489IDARToyota Corolla" => {…} "FX-34.0629025--117.6140867FX-34.0629025--117.6140867SCAR" => {…} 

    更新 – 改进了以前的代码并添加了按要求将项目转换为li元素的function。

     const carsUniq = new Map() cars.forEach($car => { const cKeys = $car.data() const { dropoffLocationId, locationId, type, vehicleExample } = cKeys const carAttrsId = dropoffLocationId + locationId + type + vehicleExample; const sCar = carsUniq.get(carAttrsId) if (!sCar) { carsUniq.set( carAttrsId, cKeys ) } else { for(const c in sCar) { if ( !sCar[c] && cKeys[c] ) sCar[c] = cKeys[c] } } }) const dasherizedCarKeys = new Map() const dasherizedData = str => { const k = dasherizedCarKeys.get(str) if (!k) { dasherizedCarKeys.set(str, 'data-' + str.replace(/([a-zA-Z])(?=[AZ])/g, '$1-').toLowerCase()) } return k } carsUniq.forEach(car => { const tCar = {} const carKeys = Object.keys(car).map(dasherizedData) for (const c in car) { tCar[dasherizedCarKeys.get(c)] = car[c] } $('
  • ').attr(tCar).appendTo("#output") })
  • 输出:

     
  • 不确定我是否理解这个问题,但是这个代码与你的方法类似,只是基于示例数据cars只有一个项目,这是数据中的第三个项目。 (各种if条件的格式化以便于查看。)

     cars = cars.reduce( ( ca, car2, ci, a ) => { let b = $.isArray( ca ) ? ca : []; a.forEach( car => { if ( ( ( car.attr( 'data-original-price' ) && car.attr( 'data-original-price' ) === car2.attr( 'data-price' ) ) || ( car2.attr( 'data-original-price' ) && car2.attr( 'data-original-price' ) === car.attr( 'data-price' ) ) ) && ( car.attr( 'data-base-price' ) !== car2.attr( 'data-base-price' ) ) && ( car.attr( 'data-price' ) !== car2.attr( 'data-price' ) ) && ( car.attr( 'data-type' ) === car2.attr( 'data-type' ) ) && ( car.attr( 'data-vehicle-example' ) === car2.attr( 'data-vehicle-example' ) ) && ( car.attr( 'data-location-id' ) === car2.attr( 'data-location-id' ) ) && ( car.attr( 'data-dropoff-location-id' ) === car2.attr( 'data-dropoff-location-id' ) ) ) { if ( car.hasClass( 'listing-prepaid' ) ) { car.find( '.column-price' ) .addClass( 'prepaid-match' ) .append( car2.find( '.column-price div.retail' ) ) .find( 'div.retail:not(.prepaid) p.button a' ) .text( 'Pay Later' ); b.push( car ); } } } ); return b; } ); 

    代码中有一个错误,比如,当你从js中的数组访问HTML元素时它返回的是字符串而不是HTML对象,所以你无法访问它意味着你不能在它上面应用JS / Jquery hasAttribute和其他函数,我已修复它,你可以在小提琴上找到工作实例,链接如下:

      var cars = ['
    • 5
    • 2

    Compact

    Nissan Versa or similarCar on Airport
    Unlimited Miles
    Pick-up
    SFB: Orlando Sanford Intl Airport
    Drop-off
    Same as pick-up
    Pay Now & Save 2%
    $25$24/day

    Pay Now

    Total: $34
  • ', '
    • 4
    • 1

    Economy

    Chevrolet Spark or similar
    Unlimited Miles
    Pick-up
    3575 Vineland Road, Orlando, FL
    Drop-off
    Same as pick-up
    Pay Now & Save 9%
    $33$29/day

    Pay Now

    Total: $36
  • ', '
    • 5
    • 2

    Compact

    Nissan Versa or similarCar on Airport
    Free CancellationPay at Pick-upUnlimited Miles
    Pick-up
    SFB: Orlando Sanford Intl Airport
    Drop-off
    Same as pick-up
    $25/day

    Select Car

    Total: $35
  • ', '
    • 2
    • 1

    Economy

    SmartCar or similarShuttle to Car
    Pay at Pick-upUnlimited Miles
    Pick-up
    MCO: Orlando Intl Airport
    Drop-off
    Same as pick-up
    $14/day

    Select Car

    Total: $28
  • ']; cars = cars.reduce((acc, car) => { let retail_match = false; cars.forEach(car2 => { if ((($(car)[0].hasAttribute("data-original-price") && $(car)[0].getAttribute("data-original-price") === $(car2)[0].getAttribute("data-price")) || ($(car2)[0].hasAttribute("data-original-price") && $(car2)[0].getAttribute("data-original-price") === $(car)[0].getAttribute("data-price"))) && ($(car)[0].getAttribute("data-base-price") != $(car2)[0].getAttribute("data-base-price")) && ($(car)[0].getAttribute("data-price") != $(car2)[0].getAttribute("data-price")) && ($(car)[0].getAttribute("data-type") == $(car2)[0].getAttribute("data-type")) && ($(car)[0].getAttribute("data-vehicle-example") == $(car2)[0].getAttribute("data-vehicle-example")) && ($(car)[0].getAttribute("data-location-id") == $(car2)[0].getAttribute("data-location-id")) && ($(car)[0].getAttribute("data-dropoff-location-id") == $(car2)[0].getAttribute("data-dropoff-location-id"))) { if (!$(car).hasClass("listing-prepaid")) retail_match = true; else { $(car).find(".column-price") .addClass("prepaid-match") .append($(car2).find(".column-price div.retail")) .find("div.retail:not(.prepaid) p.button a").text("Pay Later"); } console.log(retail_match); } }); if (!retail_match) acc.push(car); return acc; }, []); console.log(cars); [jsfiddle][1] I hope this will solve your issue, Let me know if you've any question. Thanks [1]: https://jsfiddle.net/harshsri/93qbghgk/