I want to match requests from a stream as fast as possible - javascript

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));

Related

mongoose .find ignores values returned by getters, despite toObject and toJSON getters = true in the Schema

I want to find all the documents whose getters return a particular value. Specifically, in my Position schema, I have a property called uncoveredQuantity, and I want to find all Position documents with uncoveredQuantity = 10.
When I do
const positions = await Position.find({uncoveredQuantity : 10});
it returns all documents with various different uncoveredQuantity values, not the ones that are 10! The interesting thing is that my getter function, calculateUncoveredQuantity, does get called when Position.find() gets called, and it prints the console.log statement.
function calculateUncoveredQuantity(this: PositionBaseDocument) {
console.log("getter called");
let quantityLeft = this.quantity;
const dest = this.direction === "in" ? this.closedBy : this.isCloseFor;
if (!dest) return quantityLeft;
for (const pos of dest) {
quantityLeft -= pos.amount;
}
return quantityLeft;
}
const PositionCloseSchema = {
position: {
type: mongoose.Schema.Types.ObjectId,
ref: "Position",
},
amount: {
type: Number,
min: 0,
},
};
const positionSchema = new mongoose.Schema(
{
direction: {
type: String,
enum: CONSTANTS.directions,
required: true,
},
quantity: {
type: Number,
required: true,
min: 0,
},
uncoveredQuantity: {
type: Number,
set: calculateUncoveredQuantity,
default: calculateUncoveredQuantity,
get: calculateUncoveredQuantity,
},
closedBy: {
type: [PositionCloseSchema],
required: function (this: PositionBaseDocument) {
return this.direction === "in";
},
},
isCloseFor: {
type: [PositionCloseSchema],
required: function (this: PositionBaseDocument) {
return this.direction === "out";
},
},
},
{
timestamps: true,
toJSON: {
getters: true,
},
toObject: {
getters: true
},
}
);
export const Position = mongoose.model("Position", positionSchema);

Rate exceeded error when calling AWS CloudFront update

I have to delete CloudFront distribution using AWS SDK for JavaScript so I'm trying to update it first, but when I sent the request I'm getting Rate exceeded internal server error. I tried many different values inside my config but it always returns this rate error. I'm not sure if it's problem with configuration or maybe I did something else wrong. I have also created function that creates new distribution and it works fine.
My current config object looks like this:
const originId = `S3-${buckets.build.name}`;
const originDomain = `${buckets.build.name}.s3.amazonaws.com`;
const updateParams: CloudFront.UpdateDistributionRequest = {
Id: distribution.id,
IfMatch: distribution.eTag,
DistributionConfig: {
DefaultRootObject: 'index.html',
CallerReference: caller,
Comment: 'Zilo catalog',
DefaultCacheBehavior: {
AllowedMethods: {
Items: ['HEAD', 'GET'],
Quantity: 2,
CachedMethods: {
Items: ['HEAD', 'GET'],
Quantity: 2,
},
},
TargetOriginId: originId,
ViewerProtocolPolicy: 'allow-all',
MinTTL: 3600,
SmoothStreaming: false,
Compress: false,
DefaultTTL: 5000,
FieldLevelEncryptionId: '',
MaxTTL: 10000,
LambdaFunctionAssociations: {
Quantity: 0,
Items: [],
},
ForwardedValues: {
QueryString: true,
Cookies: {
Forward: 'all',
},
Headers: {
Quantity: 0,
Items: [],
},
QueryStringCacheKeys: {
Quantity: 0,
Items: [],
},
},
},
Enabled: false,
Origins: {
Items: [
{
DomainName: originDomain,
Id: originId,
CustomOriginConfig: {
HTTPPort: 80,
HTTPSPort: 443,
OriginProtocolPolicy: 'match-viewer',
OriginSslProtocols: { Items: ['SSLv3'], Quantity: 1 },
OriginReadTimeout: 10000,
OriginKeepaliveTimeout: 10000,
},
OriginPath: '',
CustomHeaders: { Quantity: 0 },
},
],
Quantity: 1,
},
Aliases: {
Quantity: 0,
},
Logging: {
Enabled: false,
IncludeCookies: false,
Bucket: '',
Prefix: '',
},
WebACLId: '',
HttpVersion: 'http2',
Restrictions: {
GeoRestriction: {
RestrictionType: 'none',
Quantity: 0,
},
},
ViewerCertificate: {
MinimumProtocolVersion: 'SSLv3',
},
CustomErrorResponses: {
Items: [],
Quantity: 0,
},
PriceClass: 'PriceClass_100',
CacheBehaviors: { Quantity: 0, Items: [] },
},
};
Any ideas what could gone wrong here?
Ran into this same issue while attempting to disable CloudFront distribution before deleting it. Ended up finding this working solution in Java:
GetDistributionConfigResponse distributionConfigResponse =
cloudFrontClient.getDistributionConfig(GetDistributionConfigRequest.builder().id(distributionId).build());
distributionETag = distributionConfigResponse.eTag();
DistributionConfig originalConfig = distributionConfigResponse.distributionConfig();
UpdateDistributionResponse updateDistributionResponse =
cloudFrontClient.updateDistribution(r -> r.id(distributionId)
.ifMatch(distributionETag)
.distributionConfig(originalConfig.toBuilder()
.enabled(false)
.build()));

Object values not updating javascript

const displayDataTypes = [];
const TypeMap = {
data: ['type2', 'type3'],
};
const Type = {
title: 'Type',
values: [
{ label: 'Type-1', value: 'type1', disabled: false },
{ label: 'Type-2', value: 'type2', disabled: false },
{ label: 'type-3', value: 'type3', disabled: false },
{ label: 'type-4', value: 'type4', disabled: false },
{ label: 'type-5', value: 'type5', disabled: false }
]
};
const TypesSelection = TypeMap['data'];
Type.values.forEach((item) => {
const tmp = Object.create(item);
TypesSelection.forEach((type) => {
if (tmp.value !== type) {
tmp.disabled = true;
}
});
displayDataTypes.push(tmp);
});
console.log(displayDataTypes);
In the above code, Every object property disabled is getting true. I need the type2 and type3 should be false and rest should be true because of TypeMap - data.
So the output should be
[
{ label: 'Type-1', value: 'type1', disabled: true },
{ label: 'Type-2', value: 'type2', disabled: false },
{ label: 'type-3', value: 'type3', disabled: false },
{ label: 'type-4', value: 'type4', disabled: true },
{ label: 'type-5', value: 'type5', disabled: true }
]
Try it using the Array.map function:
const output = Type.values.map(v => {
v.disabled = !TypesSelection.includes(v.value);
return v;
})
console.log(output)
This is not only smaller but more readable.
The learn more about the Array.map function: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
The issue is in the iteration. At some point tmp.value will be 'type2' and type will be 'type3'. This will cause disabled to get set to true for the 'type2' object.
I think this does what you want.
const Type = {
title: 'Type',
values: [
{
label: 'Type-1',
value: 'type1',
disabled: false,
},
{
label: 'Type-2',
value: 'type2',
disabled: false,
},
{
label: 'type-3',
value: 'type3',
disabled: false,
},
{
label: 'type-4',
value: 'type4',
disabled: false,
},
{
label: 'type-5',
value: 'type5',
disabled: false,
},
],
};
const TypeMap = {
data: ['type2', 'type3']
};
const TypesSelection = TypeMap['data'];
const displayDataTypes = Type.values.map(item => {
const shallowCopy = {...item};
if (TypesSelection.every(type => (type !== shallowCopy.value))) {
shallowCopy.disabled = true;
}
return shallowCopy;
});
console.log(displayDataTypes);
I would recommend not using a property named "disable" because it causes confusing double negative situations. Instead use "enabled". This is of course assuming you have control of the property names.
If you don't need to preserve the initial "disabled" states on the copies and if you don't need copies then use the much smaller / simpler implementation in Ilijaz's answer.
Your code is failing because the correct result is being overwritten by other iterations of your inner loop.
Your current structure is 2-step:
Loop over each value in Type.values
Loop over each value in TypeMap.data and check if the two elements don't match
As a result of that second step, even elements that match will get compared to another element that doesn't match, causing the conditional code to get executed anyway.
Eg.:
Outer loop:
item is type2
Inner loop:
type is type2
check the condition: type2 !== type2 is false
Skip conditional code
Inner loop
type is now type3
check the condition: type3 !== type3 is true
run conditional code, set disabled to true
As such, all disableds will always be set to true.
Using a nested forEach isn't very well suited for this use case, since there's no straightforward way to avoid this problem. If you still want to use forEach for the outer loop, you can, or you could simplify further with map(), as #Ilijaz suggested in another answer.
Using forEach and Array.prototype.includes:
const displayDataTypes = [];
const TypeMap = {
data: ['type2', 'type3'],
};
const Type = {
title: 'Type',
values: [{
label: 'Type-1',
value: 'type1',
disabled: false,
},
{
label: 'Type-2',
value: 'type2',
disabled: false,
},
{
label: 'type-3',
value: 'type3',
disabled: false,
},
{
label: 'type-4',
value: 'type4',
disabled: false,
},
{
label: 'type-5',
value: 'type5',
disabled: false,
},
],
};
const TypesSelection = TypeMap['data'];
Type.values.forEach((item) => {
const tmp = Object.create(item);
if (!TypesSelection.includes(tmp.value)) {
tmp.disabled = true;
}
displayDataTypes.push(tmp);
});
console.log(displayDataTypes);
.as-console-wrapper {
max-height: 100% !important;
}

How to Iterate an nested dictionary using map function in javascript

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);

How to iterate a nested object using map function?

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))
)

Categories

Resources