Library or object that can check containment of lists - javascript

This question is an extension of this one: Checking containment in set of lists in javascript. I want to be able to use a set like function in nodejs or Javascript that can support checking whether or not a list belongs to a collection. For example, given the example in the link, I would like the behavior:
var s = new SetWithListCheckingAbility([[1,2], [2,3]])
s.has([2, 3])
true
I was unable to find any nodejs library that has this functionality, however. The other obvious solution seems to be JSON serializing each object that is added to the set object, and doing checking based on the JSON string, since Javascript equality works for strings. This would probably require subclassing the Set object in ES6. However, I am not sure how to do this for this case...

What you can do is take each member of the set and convert it to a string format (this answer looks like an elegant way to do that conversion from numbers to strings).
For your example, if you want s.has([3, 2]) to return false because [2,3] doesn't count as a match, the array to string conversion would look like array.join(','), otherwise array.sort().join(',') if order doesn't matter.
function setOfListsHasElement(theSet, theElement) {
let newSet = new Set();
theSet.forEach(e => newSet.add(e.join(',')) );
return newSet.has(theElement.join(','));
}
Example usage:
var theSet = new Set();
theSet.add([1,2]);
theSet.add([2,3]);
setOfListsHasElement(theSet, [2,3]); // true
setOfListsHasElement(theSet, [3,2]); // false
setOfListsHasElement(theSet, [2,6]); // false
setOfListsHasElement(theSet, ["1", "2"]); // true - don't know how you want to handle scenarios like this, where the string representation of ["1", "2"] matches that of [1,2]

I figured out how to write a custom class that does what we want:
class SetImproved extends Set{
constructor(){
super();
this.classDict = {};
this._size = 0;
}
get size(){
return this._size
}
add(x){
if(!(JSON.stringify(x) in this.classDict)){
this._size += 1;
}
this.classDict[JSON.stringify(x)] = x;
}
has(x){
return JSON.stringify(x) in this.classDict;
}
delete(x){
if(JSON.stringify(x) in this.classDict){
this._size -= 1;
}
delete this.classDict[JSON.stringify(x)];
}
clear(){
this.classDict = {};
}
keys(){
return Object.keys(this.classDict).map(x => this.classDict[x]);
}
entries(){
return Object.keys(this.classDict).map(x => this.classDict[x]);
}
}
Some examples of the functionality:
var setImproved = new SetImproved()
setImproved.add([1, "b"])
setImproved.add([2, "c"])
setImproved.add(3)
setImproved.add("asdf")
console.log(setImproved.has([1, "b"]))
console.log(setImproved.has([3]))
setImproved.delete([4])
setImproved.delete([1, "b"])
console.log(setImproved.has(3))
console.log(setImproved.entries())
console.log(setImproved.size)

Related

Comparing 2 Json Object using javascript or underscore

PS: I have already searched the forums and have seen the relevant posts for this wherein the same post exists but I am not able to resolve my issue with those solutions.
I have 2 json objects
var json1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222", addrs:"def", tab:"tab2"}];
var json2 = [{id:"tab1"},{id:"new"}];
I want to compare both these and check if the id element in json2 is present in json1 by comparing to its tab key. If not then set some boolean to false. ie by comparing id:"tab1" in json2 to tab:"tab1 in json1 .
I tried using below solutions as suggested by various posts:
var o1 = json1;
var o2 = json2;
var set= false;
for (var p in o1) {
if (o1.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
for (var p in o2) {
if (o2.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
Also tried with underscore as:
_.each(json1, function(one) {
_.each(json2, function(two) {
if (one.tab!== two.id) {
set= true;
}
});
});
Both of them fail for some test case or other.
Can anyone tell any other better method or outline the issues above.
Don't call them JSON because they are JavaScript arrays. Read What is JSON.
To solve the problem, you may loop over second array and then in the iteration check if none of the objects in the first array matched the criteria. If so, set the result to true.
const obj1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222",addrs:"def", tab:"tab2"}];
const obj2 = [{id:"tab1"},{id:"new"}];
let result = false;
for (let {id} of obj2) {
if (!obj1.some(i => i.tab === id)) {
result = true;
break;
}
}
console.log(result);
Unfortunately, searching the forums and reading the relevant posts is not going to replace THINKING. Step away from your computer, and write down, on a piece of paper, exactly what the problem is and how you plan to solve it. For example:
Calculate for each object in an array whether some object in another array has a tab property whose value is the same as the first object's id property.
There are many ways to do this. The first way involves using array functions like map (corresponding to the "calculate for each" in the question, and some (corresponding to the "some" in the question). To make it easier, and try to avoid confusing ourselves, we'll do it step by step.
function calculateMatch(obj2) {
return obj2.map(doesSomeElementInObj1Match);
}
That's it. Your program is finished. You don't even need to test it, because it's obviously right.
But wait. How are you supposed to know about these array functions like map and some? By reading the documentation. No one help you with that. You have to do it yourself. You have to do it in advance as part of your learning process. You can't do it at the moment you need it, because you won't know what you don't know!
If it's easier for you to understand, and you're just getting started with functions, you may want to write this as
obj2.map(obj1Element => doesSomeElementInObj1Match(obj1Element))
or, if you're still not up to speed on arrow functions, then
obj2.map(function(obj1Element) { return doesSomeElementInObj1Match(obj1Element); })
The only thing left to do is to write doesSomeElementInObj2Match. For testing purposes, we can make one that always returns true:
function doesSomeElementInObj2Match() { return true; }
But eventually we will have to write it. Remember the part of our English description of the problem that's relevant here:
some object in another array has a tab property whose value is the same as the first object's id property.
When working with JS arrays, for "some" we have the some function. So, following the same top-down approach, we are going to write (assuming we know what the ID is):
In the same way as above, we can write this as
function doesSomeElementInObj2Match(id) {
obj2.some(obj2Element => tabFieldMatches(obj2Element, id))
}
or
obj2.some(function(obj2Element) { return tabFieldMatches(obj2Element, id); })
Here, tabFieldMatches is nothing more than checking to make sure obj2Element.tab and id are identical.
We're almost done! but we still have to write hasMatchingTabField. That's quite easy, it turns out:
function hasMatchingTabField(e2, id) { return e2.tab === id; }
In the following, to save space, we will write e1 for obj1Element and e2 for obj2Element, and stick with the arrow functions. This completes our first solution. We have
const tabFieldMatches = (tab, id) { return tab === id; }
const hasMatchingTabField = (obj, id) => obj.some(e => tabFieldMatches(e.tab, id);
const findMatches = obj => obj.some(e => hasMatchingTabField(e1, obj.id));
And we call this using findMatches(obj1).
Old-fashioned array
But perhaps all these maps and somes are a little too much for you at this point. What ever happened to good old-fashioned for-loops? Yes, we can write things this way, and some people might prefer that alternative.
top: for (e1 of obj1) {
for (e2 of (obj2) {
if (e1.id === e2.tab) {
console.log("found match");
break top;
}
}
console.log("didn't find match);
}
But some people are sure to complain about the non-standard use of break here. Or, we might want to end up with an array of boolean parallel to the input array. In that case, we have to be careful about remembering what matched, at what level.
const matched = [];
for (e1 of obj1) {
let match = false;
for (e2 of obj2) {
if (e1.id === e2.tab) match = true;
}
matched.push(match);
}
We can clean this up and optimize it bit, but that's the basic idea. Notice that we have to reset match each time through the loop over the first object.

Checking color values without regExp

My question might be a little unclear. Same questions were also asked before but I couldn't figure out how to solve mine by reading them. I need more clear guidance:
I have already created three functions to check colors. (I don't have issues with them. I am not including them here because of the size.)
Lets assume we have three working functions like that:
function checkHex(input) {
// returns boolean value if input is hex color
}
checkHex("#1234a6"); // returns true
function checkRGB(input) {
// returns boolean value if input is RGB color
}
checkRGB("rgb(255, 255, 112)"); // returns true
function checkHSL(input) {
// returns boolean value if input is hsl color
}
checkHSL("hsl(122, 1, 1)"); // returns true
I am having 4th function (checkColor) which has mixed color values to check:
function checkColor(input) {
// returns boolean value if input belong to right color value
}
checkColor("#ccccff"); // should return true
checkColor("rgb(255,255,200)"); // should return true
checkColor("hls(46,0.66,0.21)"); // should return true
QUESTION: Do I have to include all three functions(checkHex,checkRGB,checkHSL) into the 4th one (checkColor)? How do I do that. I researched about that and tried couple ways to solve but I couldn't.
I am trying to do it without using RegExp. I am new to programming, never merged multiple functions before.
Any additional resource you can share with me about "combining multiple functions" will help me a lot too.
Thank you for your time and effort in advance !
The first 3 functions you built are often called predicates. There are a bunch of great ways to group predicates but #FelixKling mentions the simplest in the comments. You could also create functions specifically for this purpose.
// composition function
const any = (...predicates) => subject => predicates.reduce((state, predicate) => (state || predicate(subject)), false);
// predicates
const biggerThan5 = x => x>5;
const isOdd = x => !!(x % 2);
// results and usage
console.log(any(biggerThan5, isOdd)(10)); // true
console.log(any(biggerThan5, isOdd)(2)); // false
You shouldn't include functions into other functions, you just can use them inside of other ones.
For example:
function checkColor(input) {
var isHex = checkHex(input), //store the result of function
isRGB = checkRGB(input), //store the result of function
isHLS = checkHSL(input); //store the result of function
return isHex || isRGB || isHLS; //returns true if one of the options is true.
}
function checkHex(input) {
// returns boolean value if input is hex color
}
function checkRGB(input) {
// returns boolean value if input is RGB color
}
function checkHSL(input) {
// returns boolean value if input is hsl color
}

Adding meta data to a primitive in javascript

Background
We have much of our data formatted like
var X = {value:'some val',error:'maybe an error',valid:true}
as a result we find ourselves calling X.value ALL the time.
We don't use the .error or .valid nearly as much, but we do use it.
What I want
To quit calling .value everywhere, but to still have access to meta data on a per data point level.
The Question
Is there one of
A) A way to put meta data on a primitive? attaching .error to an int for example? Is it possible for bools or strings?
B) A way to make a class that can be treated as a primitive, providing a specific data member when I do? IE X.value = 5, X+3 returns 8.
C) A better design for our data? Did we just lay this out wrong somehow?
You can set the method toString() to your object and return value.
var X = {
value: 1,
error:'maybe an error',
valid:true,
toString: function() {
return this.value;
}
}
X.value = 5;
console.log(X+3);
You can represent you data as a function object that also has properties:
var X = () => 1;
X.value = 1;
X.error = 'maybe an error';
X.valid = true,
console.log(X()); // 1
console.log(X.valid); // true
For better design you can encapsulate the creation of the data object in another function.

Ember store adding attributes incorrectly

I'm using the latest version of ember-cli, ember-data, ember-localstorage-adapter, and ember.
I have a Node object which has a parent and children. Since I had issues with creating multiple relationships with the same type of object, I decided to store the parentID in a string, and the childIDs in an array of strings. However, when I create a new Node and try to add the new Node's to the parents array of IDs, the ID ends up being added to the correct parent, but also other parents.
level 1 0
/ \
level 2 1 2
| |
level 3 3 4
In a structure like this, 0, 1, and 2 all have correct child and parent IDs. However, after adding 3 and 4, node 1 and node 2's childIDs are [3, 4], instead of [3], [4] respectively.
The Array attribute:
var ArrayTransform = DS.Transform.extend({
serialize: function(value) {
if (!value) {
return [];
}
return value;
},
deserialize: function(value) {
if (!value) {
return [];
}
return value;
}
});
The insertNode code:
insert: function(elem) {
var i,
_store = elem.node.store,
newNodeJSON = elem.node.serialize();
newNodeJSON.childIds = [];
newNodeJSON.level = getNextLevel();
_store.filter('node', function(node) {
return node.get('level') === newnodeJSON.level-1;
}).then(function(prevLevelNodes) {
// if no other nodes yet
if (prevLevelNodes.toArray().length === 0) {
makeNewNode(_store, newNodeJSON, elem.node);
}
// else, generates however many nodes that are in the previous level
else {
prevLevelNodes.toArray().forEach(function(node, idx) {
newNodeJSON.parentId = node.get('id');
makeNewNode(_store, newNodeJSON, elem.node);
});
}
});
}
var makeNewNode = function(_store, newNodeJSON, node) {
console.log(newNodeJSON.parentId); // returns correct value
var newNode = _store.createRecord('node', newNodeJSON);
newNode.save();
var newNodeId = newNode.get('id');
if (newNode.get('parentId')) {
_store.find('node', newNode.get('parentId')).then(function(n) {
var cids = n.get('childIds');
console.log(newNodeId); // returns expected value
console.log(cids); // **DOESN'T RETURN AN EMPTY ARRAY**: returns array with [3,4]
cids.push(newNodeId);
console.log(n.get('childIds')); // returns array with [3,4]
n.save();
});
}
To top this off, this error happens 90% of the time, but 10% of the time it performs as expected. This seems to suggest that there's some sort of race condition, but I'm not sure where that would even be. Some places that I feel like might be causing issues: the ember-cli compilation, passing the entire _store in when making a new node, ember-data being weird, ember-localstorage-adapter being funky... no clue.
For anyone else who may have this problem in the future: the problem lies in two things.
In ArrayTransform, typically I am returning the value sans modification.
In my insert code, I'm passing the same JSON that I defined at the top of the function to makeNewNode.
This JSON contains a reference to a single childIds array; therefore, each new node that gets created uses this same reference for its childIds. Although this doesn't quite explain why the cids array wasn't empty before the push executed (perhaps this is some sort of compiler oddity or console printing lag), it explains why these both Level 3 children were in both Level 2 parents' childIds array.
tl;dr: pass by value vs pass by reference error

Sort an array by a preferred order

I'd like to come up with a good way to have a "suggested" order for how to sort an array in javascript.
So say my first array looks something like this:
['bob','david','steve','darrel','jim']
Now all I care about, is that the sorted results starts out in this order:
['jim','steve','david']
After that, I Want the remaining values to be presented in their original order.
So I would expect the result to be:
['jim','steve','david','bob','darrel']
I have an API that I am communicating with, and I want to present the results important to me in the list at the top. After that, I'd prefer they are just returned in their original order.
If this can be easily accomplished with a javascript framework like jQuery, I'd like to hear about that too. Thanks!
Edit for clarity:
I'd like to assume that the values provided in the array that I want to sort are not guaranteed.
So in the original example, if the provided was:
['bob','steve','darrel','jim']
And I wanted to sort it by:
['jim','steve','david']
Since 'david' isn't in the provided array, I'd like the result to exclude it.
Edit2 for more clarity:
A practical example of what I'm trying to accomplish:
The API will return something looking like:
['Load Average','Memory Usage','Disk Space']
I'd like to present the user with the most important results first, but each of these fields may not always be returned. So I'd like the most important (as determined by the user in some other code), to be displayed first if they are available.
Something like this should work:
var presetOrder = ['jim','steve','david']; // needn't be hardcoded
function sortSpecial(arr) {
var result = [],
i, j;
for (i = 0; i < presetOrder.length; i++)
while (-1 != (j = $.inArray(presetOrder[i], arr)))
result.push(arr.splice(j, 1)[0]);
return result.concat(arr);
}
var sorted = sortSpecial( ['bob','david','steve','darrel','jim'] );
I've allowed for the "special" values appearing more than once in the array being processed, and assumed that duplicates should be kept as long as they're shuffled up to the front in the order defined in presetOrder.
Note: I've used jQuery's $.inArray() rather than Array.indexOf() only because that latter isn't supported by IE until IE9 and you've tagged your question with "jQuery". You could of course use .indexOf() if you don't care about old IE, or if you use a shim.
var important_results = {
// object keys are the important results, values is their order
jim: 1,
steve: 2,
david: 3
};
// results is the orig array from the api
results.sort(function(a,b) {
// If compareFunction(a, b) is less than 0, sort a to a lower index than b.
// See https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort
var important_a = important_results[a],
important_b = important_results[b],
ret;
if (important_a && !important_b) {ret = -1}
else if (important_b && !important_a) {ret = 1}
else if (important_a && important_b) {ret = important_a - important_b}
else {ret = 0}; // keep original order if neither a or b is important
return(ret);
}
)
Use a sorting function that treats the previously known important results specially--sorts them to the head of the results if present in results.
items in important_results don't have to be in the results
Here's a simple test page:
<html>
<head>
<script language="javascript">
function test()
{
var items = ['bob', 'david', 'steve', 'darrel', 'jim'];
items.sort(function(a,b)
{
var map = {'jim':-3,'steve':-2,'david':-1};
return map[a] - map[b];
});
alert(items.join(','));
}
</script>
</head>
<body>
<button onclick="javascript:test()">Click Me</button>
</body>
</html>
It works in most browsers because javascript typically uses what is called a stable sort algorithm, the defining feature of which is that it preserves the original order of equivalent items. However, I know there have been exceptions. You guarantee stability by using the array index of each remaining item as it's a1/b1 value.
http://tinysort.sjeiti.com/
I think this might help. The $('#yrDiv').tsort({place:'start'}); will add your important list in the start.
You can also sort using this function the way you like.
Live demo ( jsfiddle seems to be down)
http://jsbin.com/eteniz/edit#javascript,html
var priorities=['jim','steve','david'];
var liveData=['bob','david','steve','darrel','jim'];
var output=[],temp=[];
for ( i=0; i<liveData.length; i++){
if( $.inArray( liveData[i], priorities) ==-1){
output.push( liveData[i]);
}else{
temp.push( liveData[i]);
}
}
var temp2=$.grep( priorities, function(name,i){
return $.inArray( name, temp) >-1;
});
output=$.merge( temp2, output);
there can be another way of sorting on order base, also values can be objects to work with
const inputs = ["bob", "david", "steve", "darrel", "jim"].map((val) => ({
val,
}));
const order = ["jim", "steve", "david"];
const vMap = new Map(inputs.map((v) => [v.val, v]));
const sorted = [];
order.forEach((o) => {
if (vMap.has(o)) {
sorted.push(vMap.get(o));
vMap.delete(o);
}
});
const result = sorted.concat(Array.from(vMap.values()));
const plainResult = result.map(({ val }) => val);
Have you considered using Underscore.js? It contains several utilities for manipulating lists like this.
In your case, you could:
Filter the results you want using filter() and store them in a collection.
var priorities = _.filter(['bob','david','steve','darrel','jim'],
function(pName){
if (pName == 'jim' || pName == 'steve' || pName == 'david') return true;
});
Get a copy of the other results using without()
var leftovers = _.without(['bob','david','steve','darrel','jim'], 'jim', 'steve', 'david');
Union the arrays from the previous steps using union()
var finalList = _.union(priorities, leftovers);

Categories

Resources