Set of Tuples in JavaScript? - javascript

What is the best way to implement a Set of coordinates in JavaScript? I would like to be able to do things like:
let s=new Set();
s.add([1,1]);
if (s.has([1,1])) // false, since these are different arrays
The above doesn't work, since the Set is storing a reference to the array instead of the contents.

You can subclass Set for more flexibility.
class ObjectSet extends Set{
add(elem){
return super.add(typeof elem === 'object' ? JSON.stringify(elem) : elem);
}
has(elem){
return super.has(typeof elem === 'object' ? JSON.stringify(elem) : elem);
}
}
let s=new ObjectSet();
s.add([1,1]);
console.log(s.has([1,1]))
console.log(s.has([1,2,3]));
console.log([...s]);
console.log([...s].map(JSON.parse));//get objects back

This can be done with strings:
let s=new Set();
s.add("1,1");
s.add("2,2");
console.log(s.has("1,1"), s.has("1,2")); // true false
However, I would prefer to do this with some type of numeric tuple to avoid repeated string conversion logic.

If you only plan to store pairs of coords, another possibility is to use a combination of a Map (for the first coord) and a Set (for the second coord).
function TupleSet() {
this.data = new Map();
this.add = function([first, second]) {
if (!this.data.has(first)) {
this.data.set(first, new Set());
}
this.data.get(first).add(second);
return this;
};
this.has = function([first, second]) {
return (
this.data.has(first) &&
this.data.get(first).has(second)
);
};
this.delete = function([first, second]) {
if (!this.data.has(first) ||
!this.data.get(first).has(second)
) return false;
this.data.get(first).delete(second);
if (this.data.get(first).size === 0) {
this.data.delete(first);
}
return true;
};
}
let mySet = new TupleSet();
mySet.add([0,2]);
mySet.add([1,2]);
mySet.add([0,3]);
console.log(mySet.has([1,3]));
console.log(mySet.has([0,2]));
mySet.delete([0,2]);
console.log(mySet.has([0,2]));
Unfortunately, unlike a normal Set for which:
You can iterate through the elements of a set in insertion order.
— MDN Set
This approach will, for the example above, iterate in the order:
[0,2]
[0,3]
[1,2]

I was building a game when I came across this problem.
Here's a typescript class that might be able to help you. It uses a tree to do its magic.
You should be able to easily modify this to use arrays instead of the x and y parameters
// uses an internal tree structure to simulate set-like behaviour
export default class CoordinateSet {
tree: Record<number, Record<number, boolean>> = {}
add(x: number, y: number) {
this.tree[x] ||= {}
this.tree[x][y] = true;
}
remove(x: number, y: number) {
// if the coordinate doesn't exist, don't do anything
if (!this.tree[x] || !this.tree[y]) {
return;
}
// otherwise, delete it
delete this.tree[x][y];
// if the branch has no leaves, delete the branch, too
if (!Object.keys(this.tree[x]).length) {
delete this.tree[x]
}
}
has(x: number, y: number) {
return !!this.tree[x]?.[y];
}
}
And tests, which will also show you how it works:
import CoordinateSet from "./CoordinateSet";
describe("CoordinateSet", () => {
it("Can add a coordinate", () => {
const cs = new CoordinateSet();
expect(cs.has(1,1)).toBeFalsy();
cs.add(1, 1);
expect(cs.has(1,1)).toBeTruthy();
});
it("Can remove a coordinate", () => {
const cs = new CoordinateSet();
cs.add(1, 1);
expect(cs.has(1,1)).toBeTruthy();
cs.remove(1,1);
expect(cs.has(1,1)).toBeFalsy();
})
})

If we can assume that our tuples are finite integers, they could be encoded as a float.
class TupleSet extends Set{
add(elem){
return super.add((typeof elem === 'object'
&& Number.isSafeInteger(elem[0])
&& Number.isSafeInteger(elem[1]))
? elem[0]+elem[1]/10000000 : elem);
}
has(elem){
return super.has((typeof elem === 'object'
&& Number.isSafeInteger(elem[0])
&& Number.isSafeInteger(elem[1]))
? elem[0]+elem[1]/10000000 : elem);
}
}
function TupleSetParse(elem){
return (Number.isFinite(elem)?
[Math.round(elem),Math.round((elem-Math.round(elem))*10000000)]:elem);
}
let s=new TupleSet();
s.add([1,5]);
s.add([1000000,1000000]);
s.add([-1000000,-1000000]);
console.log(s.has([1,5])); // true
console.log(s.has([1,2])); // false
console.log([...s].map(TupleSetParse));
// [ [ 1, 5 ], [ 1000000, 1000000 ], [ -1000000, -1000000 ] ]
Of course, this is limited in range. And it is fragile to some malformed input, so additional error checking should be added. However, after some testing, this method is only 25% better in speed and memory usage than the JSON.stringify approach. So, JSON is the preferred approach.

Related

I am trying to only return items in an array that contain a matching key with a true value where the key comes from a separate array

Brief example:
piece of state: ['youth', 'college'];
event Object:
{ name: theEvent,
ageDivisions: {
youth: true,
middleSchool: true,
highSchool: true,
college: true
open: false
},
}
What I want to accomplish
I want to filter through multiple event objects and only return when the event at least contains all the matching strings in the state array. this array is created based on what the user selects from the form (image Below)
My approach so far:
So far my approach has been to turn the events age divisions parameter into an array of arrays that contain the key and the value. Positions 1 and 2 respectfully
Here is the code:
codePenLink
if (filterParamaters.ageDivisions !== undefined && filterParamaters.ageDivisions.length != false) {
let parsedEvents = events.data.map(({ attributes: event }) => {
return {
...event,
...event.attributes,
ageDivisions: JSON.parse(event.ageDivisions),
eventStyles: JSON.parse(event.eventStyles),
}
})
console.log({ parsedEvents });
filteredEventss = parsedEvents.filter((event) => {
// make event .attributes.ageDivisions object an array of key value pairs
console.log({ eventThatWeAreFiltering: event });
if ((event.ageDivisions !== undefined && event.ageDivisions !== null)) {
let eventAgeDivisions = Object.entries(event.ageDivisions);
console.log({ theAgifiedObject: eventAgeDivisions });
// This maps through each object in the Object key value pair Array
// It might be easier to use map and just chang the array to only have the matching values
let onlyTrueEventAgedivisions = eventAgeDivisions.map((ageDivision, index) => {
console.log({ theAgeDivision: ageDivision[2] });
if (ageDivision[2] === true) {
return [ageDivision[0], ageDivision[2]];
} else {
return false;
}
})
console.log({ theTrueEventAgedivisions: onlyTrueEventAgedivisions });
}
})
console.log({ theFinishedFilteredevents: filteredEventss });
}
What I did was check if the ageDivisions existed in each object and if it does, run this filterParameters.ageDivisions list, checking if every property that you want to be true is set to true .
const result = parsedEvents.filter((e) => e.ageDivisions && filterParameters.ageDivisions.every(prop => e.ageDivisions[prop]))
console.log(result);

Inherited truth check in condition comparison

I'm looking for a clean way to get true or false based on an order of permissions based on the following rules:
Starts with Company Permissions as a default
Then to Team Permissions if permission defined
Finally to User Permissions if permission is
This would need to also handle undefined. So basically wanting to see if there's some "clean" way to do this without having to conditionally check each value and moving on.
In this example, the result should be false since there are no User Permissions defined and the Team Permissions has false.
const UserPermissions = {}
const TeamPermissions = {
PERMISSION_ONE: false
}
const CompanyPermissions = {
PERMISSION_ONE: true
}
const hasPermissions = UserPermissions.PERMISSION_ONE || TeamPermissions.PERMISSION_ONE || CompanyPermissions.PERMISSION_ONE
console.log(hasPermissions)
Thanks!
From my understanding, the rules are:
ignore undefined
return true of false, whatever comes first
This little function should handle that, not sure how you want to deal with empty (or all undefined) arguments.
let x;
let t = true;
let f = false;
let firstBool = (...a) => Boolean(a.filter(x => typeof x !== 'undefined').shift());
console.log(firstBool(x,t,f));
console.log(firstBool(t,x,f));
console.log(firstBool(x,f,t));
console.log(firstBool());
In your example, that would be
const hasPermissions = firstBool(
UserPermissions.PERMISSION_ONE,
TeamPermissions.PERMISSION_ONE,
CompanyPermissions.PERMISSION_ONE
]
If you are looking for the same property name in multiple objects, you might be able to slightly alter the technique in georg's answer:
const firstBoolProp = (...objs) => (prop) =>
objs.reduce((a, o) => Boolean(a) === a ? a : o[prop], undefined)
const UserPermissions = {}
const TeamPermissions = {PERMISSION_ONE: false}
const CompanyPermissions = {PERMISSION_ONE: true}
console .log (
firstBoolProp
(UserPermissions, TeamPermissions, CompanyPermissions)
('PERMISSION_ONE')
)
You can then use a single function to multiple permissions against that same set of permission objects:
const getPermissions = firstBoolProp(UserPermissions, TeamPermissions, CompanyPermissions)
const perms = {
p1: getPermissions('PERMISSION_ONE'),
p2: getPermissions('PERMISSION_TWO'),
}
//=> {p1: false, p2: undefined}
And if you want to use an array rather than individual parameters, you can simply replace (...obj) => with (obj) =>
One way to do this is to store the permissions in an array and reduce it to a boolean:
/**
*
* #param {{PERMISSION_ONE:boolean}[]} permissions
*/
function anyoneHasPermission(permissions, name = "PERMISSION_ONE") {
for (const perm of permissions) {
if (perm[name]) {
return true;
}
}
}
console.log(anyoneHasPermission([UserPermissions, TeamPermissions, CompanyPermissions]))
You need to be more specific in what you want to accomplish. Choosing the right data structure is usually more important than choosing an algorithm. Indeed, sometimes the right data structure makes the algorithm disappear completely.
Throw the permissions into an array in the order that you want them validated.
Iterate the array and if any of your conditions is not met then return false and break.
This will stop you running down the entire chain if say your top level permission is not set (because nothing beyond that matters anymore and no use validating it)
const UserPermissions = {PERMISSION_ONE: true}
const TeamPermissions = {}
const CompanyPermissions = {PERMISSION_ONE: true}
const permCheck = HasPerms([CompanyPermissions, TeamPermissions, UserPermissions]);
alert(permCheck);
function HasPerms(permArray){
for (var i = 0; i < permArray.length; i++) {
if (!permArray[i] || !permArray[i].PERMISSION_ONE) {return false;}
}
return true;
}
You can expand the function to dynamically take in the permission you would like to check but example shown hardcoded for simplicity sake.

Are Java's Streams like JavaScript's Arrays? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I try to build the Javascript equvivalent for Java's IntStream.range(0, 5).forEach(System.err::println); and reached
const IntStream = (function () {
function range(start, end, numbers = []) {
if (start === end) {
return numbers
}
return range(start + 1, end, numbers.concat(start))
}
return {
range
}
})()
IntStream.range(0, 5).forEach(number => console.log(number))
All the stream magic of Java is builtin in a normal JavaScript array. Why can't an ArrayList in Java do all same things as a Stream or is there a purpose I didn't figure out yet?
Array higher order functions will eagerly do the whole thing at each step.
const isOdd = v => v % 2 == 1;
const multiply = by => v => v * by;
const arrRange = IntStream.range(10, 20);
const arrOdd = arrRange.filter(isOdd);
const arrOddM3 = arrOdd.map(multiply(3));
Here all the bindings are distinct arrays created by each of the steps. Even when you chain them the intermediate arrays are always made and the whole array at each step need to be finished before the next can begin.
const arrOddM3 = IntStream.range(10, 20).filter(isOdd).map(multiply(3));
arrOddM3; // ==> [33, 39, 45, 51, 57]
Streams are different since they only compute values when they are accessed. A stream version would look very similar.
const streamOddM3 = Stream.range(10, Infinity).filter(isOdd).map(multiply(3));
streamOddM3; // ==> Stream
Notice I have changed the end to go to infinity. I can do that because at most it calculates the very first value and some implementations doesn't do any calculations at all until you ask for the values. To force the calculations you can take some values and get them returned as an array:
streamOddM3.take(3); // ==> [33, 39, 45]
Here is a Stream implementation loosely based on the one from the SICP videos which work similar to Java's streams.
class EmptyStream {
map() {
return this;
}
filter() {
return this;
}
take() {
return [];
}
}
class Stream extends EmptyStream {
constructor(value, next) {
super();
this._next = next;
this.value = value;
}
/**
* This prevents the value to be computed more than once
* #returns {EmptyStream|Stream}
*/
next() {
if( ! (this._next instanceof EmptyStream) ) {
this._next = this._next();
}
return this._next;
}
map(fn) {
return new Stream(fn(this.value), () => this.next().map(fn));
}
filter(fn) {
return fn(this.value) ?
new Stream(this.value, () => this.next().filter(fn)) :
this.next().filter(fn);
}
take(n) {
return n == 0 ? [] : [this.value, ...this.next().take(n && n - 1)];
}
static range(from, to, step = 1) {
if (to !== undefined && ( step > 0 && from > to || step < 0 && from < to )) {
return Stream.emptyStream;
}
return new Stream(from, () => Stream.range(from + step, to, step));
}
}
Stream.emptyStream = new EmptyStream();
There are alternatives to Stream that might work in their place.
In JavaScript you have generators (aka coroutines) and you can make a map and filter generator function that takes a generator source and becomes a new generator with that transformation. Since it is already in the language it might be a better match than Streams but I haven't studied it enough to make a generator example of the above.
In Clojure you have transducers that allows you to compose steps so that an eventual list making only happens for the elements that makes it to the final result. They are easily implemented in JavaScript.
Theres a big difference between Streams and Javasvript arrays:
[1,2,3,4]
.filter(el => {
console.log(el);
return el%2 === 0;
})
.forEach( el => console.log(el));
The result in javascript will be:
1,2,3,4 2,4
for a Stream it will be:
1,2 2 3,4 4
So as you can see javascript mutates the collection, then iterates the collection. An element passed into a Stream traverses the stream. If a collection is passed to a Stream, one element after another will be passed in the stream.
A possible Stream implementation would be:
class Stream {
constructor(){
this.queue = [];
}
//the modifying methods
forEach(func){
this.queue.push(["forEach",func]);
return this;
}
filter(func){
this.queue.push(["filter",func]);
return this;
}
map(func){
this.queue.push(["map",func]);
return this;
}
subStream(v){
this.forEach(d => v.get(d));
return this;
}
//data methods
get(value,cb){
for( let [type,func] of this.queue ){
switch(type){
case "forEach":
func(value);
break;
case "map":
value = func(value);
break;
case "filter":
if(! func(value)) return;
}
}
cb(value);
}
range(start,end){
const result = [];
Array.from({length:end-start})
.forEach((_,i)=> this.get(i+start, r => result.push(r)));
return result;
}
}
Usecase:
const nums = new Stream();
const even = new Stream();
even.filter(n => !(n%2) ).forEach(n => console.log(n));
const odd = new Stream();
even.filter(n => (n%2) ).forEach(n => console.log(n));
nums
.subStream(even)
.subStream(odd)
.range(0,100);
No they are not the same because of how they proccess the data.
In LINQ (C#) or javascript, each operation on a collection must end befor calling to the next operation in the pipeline.
In streams, its different. For example:
Arrays.asList(1,2,3).stream()
.filter((Integer x)-> x>1)
.map((Integer x)->x*10)
.forEach(System.out::println);
source collection: 1, 2 ,3
filter(1) -> You are not OK. Element 1 will not pass to the next operation
in the pipeline. Now deal with element 2.
filter(2) -> You are OK. element 2 pass to the next operation.
map(2) -> create new element 20 and put it in the new stream.
forEach(20) -> print 20. End dealing with element 2 in the source collection.
Now deal with element 3.
filter(3) -> You are OK. element 3 pass to the next operation
map(3) -> create new element 30 and put it in the new stream.
forEach(20) -> print 30. No more elements in the source collection.
finish excuting the stream.
output:
20
30
Illustration:
One of the outcome of this approach is sometimes some operations in the pipeline won't go over each element because some of them filtered out in the proccess.
This explanation were taken from: Streams In Depth By Stav Alfi

How to check if an Object already exists in an Array before adding it?

I have this algorithme issue, I would like to check if an Object is already present in my Array before adding it.
I tried many different approaches (indexOf, filter...), and my last attempt is with an angular.foreach.
The problem is my $scope.newJoin remains always empty. I understood why, it's because the if is never read, because of the 0 size of my $scope.newJoin, but I don't know how to figure this out...
$scope.newJoinTMP is composed by : 6 Objects, within each a timePosted attribute (used for compare these different array Objects).
$scope.newJoin is an empty Array. I want to fill it with the Objects inside $scope.newJoinTMP but with the certainty to have once each Objects, and not twice the same ($scope.newJoinTMP can have duplicates Objects inside, but $scope.newJoin mustn't).
angular.forEach($scope.newJoinTMP, function(item)
{
angular.forEach($scope.newJoin, function(item2)
{
if (item.timePosted === item2.timePosted)
{
//snap.val().splice(snap.val().pop(item));
console.log("pop");
}
else
{
$scope.newJoin.push(item);
console.log("newJoin :", $scope.newJoin);
}
});
});
if(!$scope.newJoin.find(el=>item.timePosted===el.timePosted){
$scope.newJoin.push(item);
console.log("newJoin :", $scope.newJoin);
}
You dont want to push inside an forEach, as it will push multiple times...
There might be better ways to handle your particular situation but here's a fix for your particular code.
Replaced your inner for each with some which returns boolean for the presence of element and by that boolean value, deciding whether to add element or not
angular.forEach($scope.newJoinTMP, function(item)
{
var isItemPresent = $scope.newJoin.some(function(item2)
{
return item.timePosted === item2.timePosted;
//you dont need this conditional handling for each iteration.
/* if (item.timePosted === item2.timePosted)
{
//snap.val().splice(snap.val().pop(item));
console.log("pop");
}
else
{
$scope.newJoin.push(item);
console.log("newJoin :", $scope.newJoin);
} */
});
if( ! isItemPresent ) {
$scope.newJoin.push(item);
} else {
//do if it was present.
}
});
If you want to avoid the nested loop (forEach, some, indexOf, or whatever) you can use an auxiliar object. It will use more memory but you will spent less time.
let arr = [{ id: 0 }, { id:0 }, { id: 1}];
let aux = {};
const result = arr.reduce((result, el) => {
if (aux[el.id] === undefined) {
aux[el.id] = null;
return [el, ...result];
} else {
return result;
}
}, []);
console.log(result);
You can use reduce
$scope.newJoin = $scope.newJoinTMP.reduce(function(c, o, i) {
var contains = c.some(function(obj) {
return obj.timePosted == o.timePosted;
});
if (!contains) {
c.push(o);
}
return c;
}, []);
The problem with your current code is, if newJoin is empty, nothing will ever get added to it - and if it isnt empty, if the first iteration doesn't match the current item being iterated from newJoinTMP - you're pushing.

Angular 2 : Typescript : Filter grid data based on provided filter condition

I am using dynamic column filtering in my grid (list). If I am going to apply static filter condition it works as expected. But when I tried it with dynamic column it don't work, some how I have to call with dynamic column as there should be multiple condition on same column also for multiple column. It doesn't throw me any error but not filter any record
Filter Panel:
Static Filter (Working):
this.GridData.filter(a => a.Scope == 'Framework');
Dynamic Filter (Not Working):
let _condition = "a.Scope == 'Framework'";
this.GridData.filter(a => _condition );
Required dynamic condition to work.
You have to call method with in the filter Function as below.
let value = 'Framework';
this.GridData.filter(this.filterData.bind(gridData , value));
filterData(value,gridData) {
return gridData.Scope == value;
}
That isn't going to work, a string is not going to be automatically converted to a JavaScript expression and evaluated.
The function, a => "a.Scope == 'Framework'", always matches all items since it returns a non-empty/non-whitespace string, which is a truthy value.
Rather than using dynamic evaluation (eval), and thereby taking on the severe security, readability, and toolability detriments that such an approach implies, I would recommend creating a fairly simple predicate builder.
In addition to being more reliable, extensible, and easier to maintain it provides a pleasant opportunity to highlight some of the elegant coding patterns that JavaScript enables and which TypeScript in turn allows us to eloquently specify.
Here's an example
export interface MemberInvocationExpression {
kind: 'memberInvocation';
memberKey: keyof Project; // project is row type
args?: {}[];
negated?: true;
conjunction: 'and' | 'or';
};
export interface MemberComparisonExpression {
kind: 'memberComparison';
memberKey: keyof Project;
compareWith: {};
negated?: true;
comparison: 'equal' | 'notEqual' | 'greater' | 'lesser';
conjunction: 'and' | 'or';
}
export type GridFilterExpression =
| MemberInvocationExpression
| MemberComparisonExpression;
export default class {
gridFilterExpressions: GridFilterExpression[] = [];
composeFilters(): (p: Project) => boolean {
return this.gridFilterExpressions.reduce((composed, expression) => {
// every element except the first element, must specify this.
const conjunction = expression.conjunction;
const predicate = predicateFromExpression(expression);
switch (conjunction) {
case 'and':
return p => composed(p) && predicate(p);
case 'or':
return p => composed(p) || predicate(p);
default:
throw Error('Invalid composition');
}
}, _ => true);
}
}
function predicateFromExpression(expression: GridFilterExpression) {
switch (expression.kind) {
case 'memberInvocation':
return predicateFromMemberInvocationExpression(expression);
case 'memberComparison':
return predicateFromMemberComparisonExpression(expression);
case // other filter expression kinds....
default: throw Error('invalid filter');
}
}
function predicateFromMemberInvocationExpression(expression: MemberInvocationExpression) {
const {memberKey, args, negated} = expression;
const predicate = (p: Project) => p[memberKey](...args);
return negated ? (p: Project) => !predicate(p) : predicate;
}
function predicateFromMemberComparisonExpression(expression: MemberComparisonExpression) {
const {memberKey, compareWith, comparison, negated} = expression;
const predicate = (p: Project) => {
switch (comparison) {
case 'equal': return p[memberKey] === compareWith;
case 'notEqual': return p[memberKey] !== compareWith;
case 'greater': return p[memberKey] > compareWith;
case 'lesser': return p[memberKey] < compareWith;
default: throw Error('Invalid comparison in filter');
}
};
return negated ? (p: Project) => !predicate(p) : predicate;
}
Populating the array, gridFilterExpressions or whatever you choose to call it, is left as an exercise to the reader, but that is the easy part.
Well, here is an example based on one of your visible filters:
gridFilterExpressions
.push({
memberKey: 'Scope',
compareWith: 'framework',
comparison: 'equal',
conjunction: 'and'
},
{
kind: 'memberInvocation',
memberKey: 'endsWith',
args: ['test'],
conjunction: 'or'
});

Categories