I am trying to iterate an dictionary using map. But i could not able to get the nested dictionary properties. For example i need the key where value.type==string.can anyone help me with this?
data.js
const products_schema = {
_id: {
auto: true
},
product_name: {
auto: false,
type: "string",
min: 5,
max: 10,
special_characters: ['_', ' '],
numbers: true,
alphabet: true,
required: true,
correct: ""
},
product_image: {
auto: false,
type: "array:string",
min: 0,
max: 50,
required: true
},
product_specification: {
auto: false,
type: "array:specification_schema",
min: 0,
max: 50,
required: true
}
}
}
let schema=new Map()
schema.set('products_schema',products_schema)
for([key,value] of schema.entries()){
console.log(value.type) //shows undefined in the console
}
The simplest way to iterate this map would be to use Object.keys() as so:
var products_schema = {
_id: {
auto: true
},
product_name: {
auto: false,
type: "string",
min: 5,
max: 10,
special_characters: ['_', ' '],
numbers: true,
alphabet: true,
required: true,
correct: ""
},
product_image: {
auto: false,
type: "array:string",
min: 0,
max: 50,
required: true
},
product_specification: {
auto: false,
type: "array:specification_schema",
min: 0,
max: 50,
required: true
}
}
Object.keys(products_schema).forEach(key => console.log(products_schema[key].type));
I do not know why you need to use the Map object, but for this case of iterating through objects, you can try the good old for..in loop, which iterates through the enumerable properties of your products_schema object.
const products_schema = {
_id: {
auto: true
},
product_name: {
auto: false,
type: "string",
min: 5,
max: 10,
special_characters: ['_', ' '],
numbers: true,
alphabet: true,
required: true,
correct: ""
},
product_image: {
auto: false,
type: "array:string",
min: 0,
max: 50,
required: true
},
product_specification: {
auto: false,
type: "array:specification_schema",
min: 0,
max: 50,
required: true
}
};
for (const key in products_schema){
console.log(key);
for (const inner in products_schema[key]){
console.log(`${inner}:${products_schema[key][inner]}`);
}
}
Using Map() isn't always the best solution, although I have to admit in circumstances where you know the name of the key you want it's great, but to get a birds-eye view of a Map's content is a little more involved.
In the following demo we will use:
objToMap(object): Using Object.entries() and destructure the result within a for...of loop. Each iteration is a simple .set(). A complete Map is returned.
displayMap(Map): Displaying all paths of the Map we'll use two for..of loops. The outer loop will address each key/value of Map. The inner loop will address each key/value of the value extracted from the outer loop.
getPath(Map, root, prop): The second parameter is the Map key (ex. 'product_name'). The third parameter is the key within the object of second parameter (ex. 'type'). Once the second parameter is found with Map.get() a destructuring for...of loop of Map.entries() is filtered by .filter() and the result from Object.fromEntries() is returned.
let products_schema = {
_id: {
auto: true
},
product_name: {
auto: false,
type: "string",
min: 5,
max: 10,
special_characters: ['_', ' '],
numbers: true,
alphabet: true,
required: true,
correct: ""
},
product_image: {
auto: false,
type: "array:string",
min: 0,
max: 50,
required: true
},
product_specification: {
auto: false,
type: "array:specification_schema",
min: 0,
max: 50,
required: true
}
}
function objToMap(obj) {
let map = new Map();
for (let [key, value] of Object.entries(obj)) {
map.set(key, value);
}
return map;
}
let x = objToMap(products_schema);
// Display all map paths
function displayMap(Map) {
for (let [key, value] of Map.entries()) {
for (let [prop, val] of Object.entries(value)) {
console.log(key + ': ' + prop + ': ' + val);
}
}
}
displayMap(x);
function getPath(Map, root, prop) {
let level1 = Map.has(root) ? Map.get(root) : {};
const filtered = Object.entries(level1)
.filter(([key, value]) => key.includes(prop));
return Object.fromEntries(filtered);
}
let y = getPath(x, 'product_name', 'type');
console.log(y);
// if root or prop is not found an empty object is returned
let z = getPath(x, 'product_name', 'foo');
console.log(z);
Related
I need to parse through and look for a specific id. In the code below I need to be able to pull out the id number. It looks like this "itemIds":["918e337d-82ae-4e91-bdc3-16ad06572e21". I need to be able to pull the number "918e337d-82ae-4e91-bdc3-16ad06572e21". I have been having trouble understanding this concept. If you could send how or the actual code to do it . That would be very much appreciated.
{"dbSessionTokenMap":{"CXO_PC_ST":"e5b96399-fefc-4d9d-93ba-2aa1059008ce|{\"mtoken\":\"301:12#90271897#2=60818072#7=100439087\"}"},"id":"e5b96399-fefc-4d9d-93ba-2aa1059008ce","checkoutFlowType":"Guest","cartId":"ffd6cb2f-efc2-47b2-96d9-52d2cfb3d69b","items":[{"id":"918e337d-82ae-4e91-bdc3-16ad06572e21","offerId":"864A02B3BF7442A4802E6DF7BA2EDA28","productId":"1ZPTYHZN85S6","productName":"Pokemon Assorted Lot of 50 Single Cards [Any Series]","itemId":127446742,"sellerId":"A577588AB81D43AE9E7F468183B3568A","thumbnailUrl":"https://i5.walmartimages.com/asr/aa6ed747-9cd0-44dc-b927-44bc2b7e1ca7_1.62c435484d4015af1c325e9cdeeb3662.jpeg?odnHeight=100&odnWidth=100&odnBg=FFFFFF","legacySellerId":3340,"productClassType":"REGULAR","quantity":1,"unitPrice":8.61,"type":"REGULAR","price":8.61,"unitOfMeasure":"EA","hasCarePlan":false,"brand":"Pok?mon","discount":{},"rhPath":"20000:25000:25003:25114:25333","isWarrantyEligible":false,"category":"0:4171:3318550:617941:8920388","primaryCategory":"Home Page/Toys/Shop Toys by Age/Toys for Kids 5 to 7 Years/Toys for Kids 5 to 7 Years","isCarePlan":false,"isEgiftCard":false,"isAssociateDiscountEligible":false,"isShippingPassEligible":false,"isTwoDayShippingEligible":false,"classId":"5","maxQuantityPerOrder":100,"isSubstitutable":false,"isInstaWatch":false,"isAlcoholic":false,"isSnapEligible":false,"isAgeRestricted":false,"isSubstitutionsAllowed":false,"fulfillmentSelection":{"fulfillmentOption":"S2H","shipMethod":"STANDARD","availableQuantity":172},"servicePlanType":"NONE","errors":[],"wfsEnabled":false,"isAlcohol":false}],"shipping":{"postalCode":"82001","city":"CHEYENNE","state":"WY"},"promotions":[{"promotionId":"1c2cbad1-205e-425f-9297-8629d68e97f6","okToPayAwards":[{"applyTo":"CART_FULFILLMENT_PRICE","actionType":"AWARD","name":"DS_Donors_Choose_Teachers_Card","awardType":"OK_TO_PAY","description":"DonorsChoose Card","applicableTo":{"ITEM_TAX":true,"SHIP_PRICE":true,"SHIP_TAX":true,"FEE":true,"ITEM_PRICE":true},"asset":{"image":"https://i5.walmartimages.com/dfw/63fd9f59-e0cf/455269aa-c4e8-46a5-8d76-5d4b458e1269/v1/Select_gift_card.png","imageAlt":""},"awardEligibleItemIds":[],"awardEligibleTotalsByItemId":{}}],"dsEligibleItemIds":[],"dsEligibleTotals":{}}],"summary":{"subTotal":8.61,"shippingIsEstimate":false,"taxIsEstimate":true,"grandTotal":8.61,"quantityTotal":1,"amountOwed":8.61,"merchandisingFeesTotal":0,"shippingCosts":[{"label":"Top Cut Central shipping","type":"marketplace_shipping","cost":0.0}],"shippingTotal":0.0,"hasSurcharge":false,"preTaxTotal":8.61,"addOnServicesTotal":0,"itemsSubTotal":8.61},"pickupPeople":[],"email":"","buyer":{"customerAccountId":"9afb345e-74b8-4afb-93d0-4bf52697e18f","isGuestSignupRequired":false,"isGuest":true,"isAssociate":false,"applyAssociateDiscount":false},"allowedPaymentTypes":[{"type":"CREDITCARD","cvvRequired":true},{"type":"PAYPAL","cvvRequired":false},{"type":"GIFTCARD","cvvRequired":false},{"type":"VISA_CHECKOUT","cvvRequired":false},{"type":"MASTERPASS","cvvRequired":false},{"type":"CHASEPAY","cvvRequired":false},{"type":"AMEX_CHECKOUT","cvvRequired":false}],"registries":[],"payments":[],"cardsToDisable":[],"allowedPaymentPreferences":[],"isRCFEligible":false,"isMarketPlaceItemsExist":true,"version":"v3","shippingCategory":{"shippingGroups":[{"itemIds":["918e337d-82ae-4e91-bdc3-16ad06572e21"],"seller":"Top Cut Central","defaultSelection":true,"fulfillmentOption":"S2H","shippingGroupOptions":[{"method":"EXPEDITED","methodDisplay":"Expedited","selected":false,"charge":8.99,"deliveryDate":1606766400000,"availableDate":1606766400000,"fulfillmentOption":"S2H","onlineStoreId":0,"isThresholdShipMethod":false},{"method":"STANDARD","methodDisplay":"Standard","selected":true,"charge":0.0,"deliveryDate":1606939200000,"availableDate":1606939200000,"fulfillmentOption":"S2H","onlineStoreId":0,"isThresholdShipMethod":false}],"isEdelivery":false,"hasWFSItem":false,"itemSellerGroups":[]}]},"entityErrors":[],"oneDaySelected":false,"paymentWithBagFee":false,"giftDetails":{"giftOrder":false,"hasGiftEligibleItem":false,"xoGiftingOptIn":false},"canApplyDetails":[],"dbName":"e5b96399-fefc-4d9d-93ba-2aa1059008ce|C","jwt":"eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI1MjdmZTRjYi0wZjI5LTRjZWYtOWRiOS00Yzc1YWQ5MTMwNTQiLCJpYXQiOjE2MDYwOTY0NjMsImlzcyI6IjU3YjM0ZTNhZGE1MjkzMGEwYzBjYTFjOSIsIk9yZ1VuaXRJZCI6IjU2ZWJiMTJkZGE1MjkzMWRhOGZlMDc5YSIsIlJlZmVyZW5jZUlkIjoiZTViOTYzOTktZmVmYy00ZDlkLTkzYmEtMmFhMTA1OTAwOGNlIn0.-ta5UQLkJtXNR5yP2dOhDiDMF9dPpbfktAJu7z22kNM"}
Edit Below! Edit Below! Edit Below!
For future visitors -
I like to use split to find what I specifically need. For example for this problem I had above. I would just use
let myId = string.split(`"itemIds":["`)[1].split('"')[0]
This should work well and I use this method all the time. If you have any better methods feel free to reply or leave an answer. You can also use JSON.parse(your data) and look for your specific variable that way. This article should also help you understand how to use it. https://www.tutorialrepublic.com/javascript-tutorial/javascript-json-parsing.php
First you need to determine which prop will you get the value of? And then try this, in this case I will get "itemIds" so my findProp function will take 2 parameters:
const myObj = {
dbSessionTokenMap: {
CXO_PC_ST:
'e5b96399-fefc-4d9d-93ba-2aa1059008ce|{"mtoken":"301:12#90271897#2=60818072#7=100439087"}',
},
id: "e5b96399-fefc-4d9d-93ba-2aa1059008ce",
checkoutFlowType: "Guest",
cartId: "ffd6cb2f-efc2-47b2-96d9-52d2cfb3d69b",
items: [
{
id: "918e337d-82ae-4e91-bdc3-16ad06572e21",
offerId: "864A02B3BF7442A4802E6DF7BA2EDA28",
productId: "1ZPTYHZN85S6",
productName: "Pokemon Assorted Lot of 50 Single Cards [Any Series]",
itemId: 127446742,
sellerId: "A577588AB81D43AE9E7F468183B3568A",
thumbnailUrl:
"https://i5.walmartimages.com/asr/aa6ed747-9cd0-44dc-b927-44bc2b7e1ca7_1.62c435484d4015af1c325e9cdeeb3662.jpeg?odnHeight=100&odnWidth=100&odnBg=FFFFFF",
legacySellerId: 3340,
productClassType: "REGULAR",
quantity: 1,
unitPrice: 8.61,
type: "REGULAR",
price: 8.61,
unitOfMeasure: "EA",
hasCarePlan: false,
brand: "Pok?mon",
discount: {},
rhPath: "20000:25000:25003:25114:25333",
isWarrantyEligible: false,
category: "0:4171:3318550:617941:8920388",
primaryCategory:
"Home Page/Toys/Shop Toys by Age/Toys for Kids 5 to 7 Years/Toys for Kids 5 to 7 Years",
isCarePlan: false,
isEgiftCard: false,
isAssociateDiscountEligible: false,
isShippingPassEligible: false,
isTwoDayShippingEligible: false,
classId: "5",
maxQuantityPerOrder: 100,
isSubstitutable: false,
isInstaWatch: false,
isAlcoholic: false,
isSnapEligible: false,
isAgeRestricted: false,
isSubstitutionsAllowed: false,
fulfillmentSelection: {
fulfillmentOption: "S2H",
shipMethod: "STANDARD",
availableQuantity: 172,
},
servicePlanType: "NONE",
errors: [],
wfsEnabled: false,
isAlcohol: false,
},
],
shipping: { postalCode: "82001", city: "CHEYENNE", state: "WY" },
promotions: [
{
promotionId: "1c2cbad1-205e-425f-9297-8629d68e97f6",
okToPayAwards: [
{
applyTo: "CART_FULFILLMENT_PRICE",
actionType: "AWARD",
name: "DS_Donors_Choose_Teachers_Card",
awardType: "OK_TO_PAY",
description: "DonorsChoose Card",
applicableTo: {
ITEM_TAX: true,
SHIP_PRICE: true,
SHIP_TAX: true,
FEE: true,
ITEM_PRICE: true,
},
asset: {
image:
"https://i5.walmartimages.com/dfw/63fd9f59-e0cf/455269aa-c4e8-46a5-8d76-5d4b458e1269/v1/Select_gift_card.png",
imageAlt: "",
},
awardEligibleItemIds: [],
awardEligibleTotalsByItemId: {},
},
],
dsEligibleItemIds: [],
dsEligibleTotals: {},
},
],
summary: {
subTotal: 8.61,
shippingIsEstimate: false,
taxIsEstimate: true,
grandTotal: 8.61,
quantityTotal: 1,
amountOwed: 8.61,
merchandisingFeesTotal: 0,
shippingCosts: [
{
label: "Top Cut Central shipping",
type: "marketplace_shipping",
cost: 0.0,
},
],
shippingTotal: 0.0,
hasSurcharge: false,
preTaxTotal: 8.61,
addOnServicesTotal: 0,
itemsSubTotal: 8.61,
},
pickupPeople: [],
email: "",
buyer: {
customerAccountId: "9afb345e-74b8-4afb-93d0-4bf52697e18f",
isGuestSignupRequired: false,
isGuest: true,
isAssociate: false,
applyAssociateDiscount: false,
},
allowedPaymentTypes: [
{ type: "CREDITCARD", cvvRequired: true },
{ type: "PAYPAL", cvvRequired: false },
{ type: "GIFTCARD", cvvRequired: false },
{ type: "VISA_CHECKOUT", cvvRequired: false },
{ type: "MASTERPASS", cvvRequired: false },
{ type: "CHASEPAY", cvvRequired: false },
{ type: "AMEX_CHECKOUT", cvvRequired: false },
],
registries: [],
payments: [],
cardsToDisable: [],
allowedPaymentPreferences: [],
isRCFEligible: false,
isMarketPlaceItemsExist: true,
version: "v3",
shippingCategory: {
shippingGroups: [
{
itemIds: ["918e337d-82ae-4e91-bdc3-16ad06572e21"],
seller: "Top Cut Central",
defaultSelection: true,
fulfillmentOption: "S2H",
shippingGroupOptions: [
{
method: "EXPEDITED",
methodDisplay: "Expedited",
selected: false,
charge: 8.99,
deliveryDate: 1606766400000,
availableDate: 1606766400000,
fulfillmentOption: "S2H",
onlineStoreId: 0,
isThresholdShipMethod: false,
},
{
method: "STANDARD",
methodDisplay: "Standard",
selected: true,
charge: 0.0,
deliveryDate: 1606939200000,
availableDate: 1606939200000,
fulfillmentOption: "S2H",
onlineStoreId: 0,
isThresholdShipMethod: false,
},
],
isEdelivery: false,
hasWFSItem: false,
itemSellerGroups: [],
},
],
},
entityErrors: [],
oneDaySelected: false,
paymentWithBagFee: false,
giftDetails: {
giftOrder: false,
hasGiftEligibleItem: false,
xoGiftingOptIn: false,
},
canApplyDetails: [],
dbName: "e5b96399-fefc-4d9d-93ba-2aa1059008ce|C",
jwt:
"eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI1MjdmZTRjYi0wZjI5LTRjZWYtOWRiOS00Yzc1YWQ5MTMwNTQiLCJpYXQiOjE2MDYwOTY0NjMsImlzcyI6IjU3YjM0ZTNhZGE1MjkzMGEwYzBjYTFjOSIsIk9yZ1VuaXRJZCI6IjU2ZWJiMTJkZGE1MjkzMWRhOGZlMDc5YSIsIlJlZmVyZW5jZUlkIjoiZTViOTYzOTktZmVmYy00ZDlkLTkzYmEtMmFhMTA1OTAwOGNlIn0.-ta5UQLkJtXNR5yP2dOhDiDMF9dPpbfktAJu7z22kNM",
};
const findProp = (obj, prop, out) => {
let i,
proto = Object.prototype,
ts = proto.toString,
hasOwn = proto.hasOwnProperty.bind(obj);
if ("[object Array]" !== ts.call(out)) {
out = [];
}
for (i in obj) {
if (hasOwn(i)) {
if (i === prop) {
out.push(obj[i]);
} else if (
"[object Array]" === ts.call(obj[i]) ||
"[object Object]" === ts.call(obj[i])
) {
findProp(obj[i], prop, out);
}
}
}
return out;
};
console.log(findProp(myObj, "itemIds"));
Here is a data structure I'm working with
masterObject: {
Steps: [
{
step: {
required: false,
},
step: {
required: false,
},
step: {
required: false,
},
},
]
}
I'd like to loop through Steps array and check if all each step object contains propery required that is equal to false.
If all required steps are false I'd like to attach a property to the masterObject object of
masterObject.isObjectWithNoRequiredSteps = true;
which would look like this after the code runs
masterObject: {
Steps: [
{
step: {
required: false,
},
step: {
required: false,
},
step: {
required: false,
},
},
]
isObjectWithNoRequiredSteps: true
}
If theres an example where one of the step object required is true I'd like to set the new property to false
masterObject.isObjectWithNoRequiredSteps = false;
masterObject: {
Steps: [
{
step: {
required: true,
},
step: {
required: false,
},
step: {
required: false,
},
},
]
isObjectWithNoRequiredSteps: false
}
What is the best approach to setting this property on the higher level object?
The object inside Steps array has multiple attributes/keys with the same name step. This is not allowed in JavaScript. Consider changing your data structure as follows
masterObject: {Steps: [{
step1: { required: false, },
step2: { required: false, },
step3: { required: false, },
}, ] }
Now, to determine if each step inside Steps array is false, use following code
masterObject.isObjectWithNoRequiredSteps = Object.keys(masterObject.Steps[0]).reduce(function(result, nextStep){
const isRequired = masterObject.Steps[0][nextStep].required;
return !isRequired && result;
}, true);
I had used for loop to iterate nested objects, I am trying to replace forEach with the map function, without success. Can anyone help me with this?
schema.js
const products_schema = {
product_name: {
auto: false,
type: "string",
min: 5,
max: 10,
special_characters: ['_', ' '],
numbers: true,
alphabet: true,
required: true,
correct: ""
},
product_image: {
auto: false,
type: "array:string",
min: 0,
max: 50,
required: true
}
}
const specification_schema = {
brand: {
auto: false,
type: "string",
min: 10,
max: 50,
special_characters: ['_', ' '],
numbers: true,
alphabet: true,
required: true
}
}
let schema = {
products_schema:products_schema,
specification_schema:specification_schema
}
for(var key in schema)
{
var value = schema[key]
Object.keys(value).forEach(key => console.log(value[key].type));
}
"Expected output:"
string
array:string
string
use Object.values then use map to return only type property.
const products_schema = {
product_name: {
auto: false,
type: "string",
min: 5,
max: 10,
special_characters: ['_', ' '],
numbers: true,
alphabet: true,
required: true,
correct: ""
},
product_image: {
auto: false,
type: "array:string",
min: 0,
max: 50,
required: true
}
}
const specification_schema = {
brand: {
auto: false,
type: "string",
min: 10,
max: 50,
special_characters: ['_', ' '],
numbers: true,
alphabet: true,
required: true
}
}
let schema = {
products_schema:products_schema,
specification_schema:specification_schema
}
const mergedObjects = {...products_schema, ...specification_schema};
const output = Object.values(mergedObjects).map(({type}) => type);
console.log(output);
You could use nested Object.values():
const products_schema={product_name:{auto:false,type:"string",min:5,max:10,special_characters:['_',' '],numbers:true,alphabet:true,required:true,correct:""},product_image:{auto:false,type:"array:string",min:0,max:50,required:true}},
specification_schema={brand:{auto:false,type:"string",min:10,max:50,special_characters:['_',' '],numbers:true,alphabet:true,required:true}},
schema={ products_schema, specification_schema }
Object.values(schema).forEach(o => {
Object.values(o).forEach(a => console.log(a.type))
})
If you want to get an array of nested type you could use flatMap
const products_schema={product_name:{auto:false,type:"string",min:5,max:10,special_characters:['_',' '],numbers:true,alphabet:true,required:true,correct:""},product_image:{auto:false,type:"array:string",min:0,max:50,required:true}},
specification_schema={brand:{auto:false,type:"string",min:10,max:50,special_characters:['_',' '],numbers:true,alphabet:true,required:true}},
schema={ products_schema, specification_schema }
const types = Object.values(schema).flatMap(o =>
Object.values(o).map(a => a.type)
)
console.log(types)
If flatMap is not supported, you could simply use the first snippet and push to an array instead of logging it to the console.
const output = [];
Object.values(schema).forEach(o =>
Object.values(o).forEach(a => output.push(a.type))
)
I have a scenario in which I need to display the data in an increasing order of the index number.
myArray = [
{custom_carousel: false, default_label: "SmartCards", index: 3, visible: true},
{custom_carousel: false, default_label: "Pathways", index: 2, visible: false},
{custom_carousel: false, default_label: "Pathways", index: 1, visible: false},
{custom_carousel: false, default_label: "Pathways", index: 0, visible: false}
]
Should I first sort the array or add if else if condition ?
if(index === 0){
}else if (index === 1){
}else if (index === 2){
}else if (index === 3){
}
You can use the sort method based on index values:
myArray = [
{custom_carousel: false, default_label: "Pathways", index: 2, visible: false},
{custom_carousel: false, default_label: "SmartCards", index: 3, visible: true},
{custom_carousel: false, default_label: "Pathways", index: 1, visible: false},
{custom_carousel: false, default_label: "Pathways", index: 0, visible: false}
]
console.log(myArray.sort((a,b)=>a.index - b.index));
myArray.sort(function(a, b){return a.index - b.index});
Will sort your array by index
Sorting will take O(n+log(n)) time and if-else-if-else will take O(n2) time. So sorting is better choice.
I suggest to sort the array in advance and then access by the index of the array.
Reasons
easy to maintain with more elements of the array
easy to access in wanted order
code ie easy to understand, instead of having some code, which reason is not obvious and requieres explanation
access is fast, instead of iterating the array always from the beginning for getting the next item.
var myArray = [{ custom_carousel: false, default_label: "SmartCards", index: 3, visible: true }, { custom_carousel: false, default_label: "Pathways", index: 2, visible: false }, { custom_carousel: false, default_label: "Pathways", index: 1, visible: false }, { custom_carousel: false, default_label: "Pathways", index: 0, visible: false }]
myArray.sort(({ index: a }, { index: b }) => a - b);
console.log(myArray);
I am using a Map to match incoming requests. I want to match those requests with their pair as soon as I get them. Order matters because it's first to come, first to match. Avoiding unnecessary operations is a requirement. My understanding is hash maps are faster than arrays iterations, and Maps maintain order. What is the best implementation for matching streaming objects with little to no space or time complexity? The data structure is not set in stone, it can be modified and encoded in whatever format optimizes, as long as the information is not lost. From my understanding, the best that can be achieved is O(n). Another problem I am facing with hashing is overwriting duplicates in the queue. This is what I have.
function* match(items, identity, remaining = new Map()) {
for (let item of items) {
let id = identity(item);
let pair = x =>({type:x.type==="passenger"? "driver": "passenger", direction:x.direction, time:x.time})
let key = item=> item.type + item.direction + item.time;
let candidate = remaining.get(key(pair(id)));
if (candidate) {
remaining.delete(key(pair(id)));
yield [item, candidate];
} else {
remaining.set(key(id), item);
}
}
}
// Example:
let items = [{
type: 'driver',
direction: 'east',
time: '9:15',
name:['Archibald Trump']
},{
type: 'passenger',
direction: 'east',
time: '9:15',
name:['Bacon Eater']
},{
type: 'passenger',
direction: 'east',
time: '9:15',
name:['Banjo Barney']
},{
type: 'passenger',
direction: 'east',
time: '9:15',
name:['Flimsy Stick']
}, {
type: 'passenger',
direction: 'west',
time: '9:30',
name:['Big Red']
},{
type: 'passenger',
direction: 'west',
time: '9:35',
name:['Hathaway Anne']
}];
let remaining = new Map();
let pairs = match(items, item => item, remaining);
console.log('pairs',...pairs);
console.log('remaining',...remaining.values());
Encapsulation & Space Complexity:
Your usage of Set is appropriate, but the code can be improved by encapsulating the matching functionality and getting rid of globals. I suggest a generator which yields matching pairs as follows with reduced space complexity:
// Match items with first equal match:
function* match(items, equals, remaining = new Set()) {
items:
for (let item of items) {
for (let candidate of remaining) {
if (equals(item, candidate)) {
remaining.delete(candidate);
yield [item, candidate];
continue items;
}
}
remaining.add(item);
}
}
// Example:
let items = [1, 2, 5, 3, 3, 4, 2, 1, 4];
let remaining = new Set();
let pairs = match(items, (a, b) => a == b, remaining);
console.log(...pairs);
console.log(...remaining);
Depending on your items, you would need to supply a matching equals callback. In your case:
let pairs = match(
items,
(a, b) =>
a.type !== b.type &&
a.direction === b.direction &&
a.time === b.time,
remaining
);
Optimization:
If you could map requests to primitive identity values, you could replace the for (let i of memo) { ... } loop with a simple map lookup. The time complexity to pair n items then reduces to O(n).
However, since you are not matching 'identical' items but items with opposing type property, this optimization is not directly applicable. You need to add another callback which gives us the expected id of a matching item.
Also, since you might encounter multiple request with identical identity, you need a multimap:
// Multimap insertion:
function insert(map, key, value) {
let values = map.get(key);
if (values) values.push(value);
else map.set(key, [value]);
}
// Multimap retrieval:
function retrieve(map, key) {
let values = map.get(key);
if (values) {
let value = values.pop();
if (values.length === 0) map.delete(key);
return value;
}
}
// Match items with first identical match:
function* match(items, identity, match, remaining) {
for (let item of items) {
let candidate = retrieve(remaining, match(item));
if (candidate) yield [item, candidate];
else insert(remaining, identity(item), item);
}
}
// Example:
let items = [{
type: 'driver',
direction: 'east',
time: '9:15',
name: ['Archibald Trump']
}, {
type: 'passenger',
direction: 'east',
time: '9:15',
name: ['Bacon Eater']
}, {
type: 'passenger',
direction: 'east',
time: '9:15',
name: ['Banjo Barney']
}, {
type: 'passenger',
direction: 'east',
time: '9:15',
name: ['Flimsy Stick']
}, {
type: 'passenger',
direction: 'west',
time: '9:30',
name: ['Big Red']
}, {
type: 'passenger',
direction: 'west',
time: '9:35',
name: ['Hathaway Anne']
}];
let remaining = new Map();
let pairs = match(
items,
item => '' + (item.type == 'driver') + item.direction + item.time,
item => '' + (item.type == 'passenger') + item.direction + item.time,
remaining
);
console.log(...pairs);
console.log(...remaining.values());
The runtime complexity of above implementation is not easily determined as it depends on the runtime complexity of Array.shift() and Array.push(). However, if we assume that such key collisions are rare or assume that the JavaScript engine executed both of these methods in amortized constant time, we could still expect an O(n) runtime complexity for n items.
Maps are faster than arrays. You can access an map key much quicker than looping over an array every time. Here is your example using an map.
var pairs = [];
var memo = {};
function pair(item) {
var key = item.type.toString() + item.direction + item.time;
if(memo[key]) {
pairs.push(item);
delete memo[key];
} else {
memo[key] = item;
}
}
var ar = [{
type: true,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: true,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: true,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: true,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: true,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: true,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}, {
type: false,
direction: false,
time: '9:00'
}];
for (let it of ar) {
pair(it);
}
console.log('matching pairs: ', pairs);
console.log('left over nonmatching pairs:', Object.values(memo));