js object problem - javascript

I use an object to check that a group of radio buttons have a precise value like set on "rule" object. Here is an example:
arr = {a:"1", b:"1", c:"1", c:"2"}; //that's my object rule
var arr2={}; //here I create my second array with charged value
$("#form_cont input:checked").each(function()
{
arr2[$(this).attr("name")]=$(this).val();
});
//here I make the check
for (k in arr2)
{
if (typeof arr[k] !== 'undefined' && arr[k] === arr2[k])
{
$("#form_cont li[name$='"+k+"']").css('background-color', '');
}
else
{
$("#form_cont li[name$='"+k+"']").css('background-color', 'pink');
}
}
The problem is when I have to check the "c" key I get last the one (2) and not the right value how that may e 1 or 2
thanks in advance
ciao, h.

In order to have more than one value, arr's property c will need to be an array:
arr = {a:["1"], b:["1"], c:["1","2"]}; //that's my object rule
Of course, your validity check must also change to search the new array:
typeof arr[k] !== 'undefined' && contains(arr[k], arr2[k])
...
function contains(a, obj){
for(var i = 0; i < a.length; i++) {
if(a[i] === obj){
return true;
}
}
return false;
}

You cannot have two properties on an object that are named the same. Thus when the javascript compiler sees the line arr = {a:"1", b:"1", c:"1", c:"2"}; it automatically changes it to arr = {a:"1", b:"1", c:"2"}; letting the last definition of c overwrite the first one

Related

how to get value from object with a partial index value javascript

I have an object which contains alot of keys and values. I can get any value using the index. But I dont have the full index, I have a part of it, would I be able to get the value based on a part of the index.
Example:
c = {'select':'MyValue',...}
I can get the value using indexing as shown below:
c['select'] = 'MyValue'
I tried to create this function which searches exact value:
function search(nameKey, c){
for (var i=0; i < c.length; i++) {
if (c[i].select === nameKey) {
return c[i];
}
}
}
c['select'] will return 'MyValue' but I need to do something like c['Sel'] or c['select'] or c['Select']or c['selected']to return the same 'MyValue'
Well the logic doesn't seem to be very clear and it's not quite relevant how it would be matching the key.
But This is a function that may help in the specific cases you showed:
function search(nameKey, obj) {
if (obj.hasOwnProperty(nameKey)) {
return obj[nameKey];
} else {
var res = Object.keys(obj).filter(function(k) {
return (k.toLowerCase().indexOf(nameKey.toLowerCase()) > -1) || (nameKey.toLowerCase().indexOf(k.toLowerCase()) > -1);
});
return res ? obj[res] : false;
}
}
Explanation:
First we use Object#hasOwnProperty() to check if the object has the searched name as key/property, we return it's value, this will avoid looping all the keys.
Otherwise we use Object.keys() to get the keys of the object.
Then we use Array#filter() method over the keys array to check if a relevant key exists we
return it's value, otherwise we return false.
Demo:
function search(nameKey, obj) {
if (obj.hasOwnProperty(nameKey)) {
return obj[nameKey];
} else {
var res = Object.keys(obj).filter(function(k) {
return (k.toLowerCase().indexOf(nameKey.toLowerCase()) > -1) || (nameKey.toLowerCase().indexOf(k.toLowerCase()) > -1);
});
return res ? obj[res] : false;
}
}
var c = {
'select': 'MyValue'
};
console.log(search("Sel", c));
Here's an one liner (!):
Assuming your array is in data and the partial index value is in selector:
const result = Object.keys(data).filter(k => k.toLowerCase().indexOf(selector.toLowerCase()) != -1).map(k => data[k]);
The above code returns an Array (coz, there may be more than one match). If you just need a first element, just do result[0].
You can use Object.keys() to get an array of the property names.
Then find first match using Array#find() to get the key needed (if it exists)
const data = {
aaaa: 1,
bbbbbbb: 2,
cccc: 3
}
function search(nameKey, obj) {
nameKey = nameKey.toLowerCase();// normalize both to lowercase to make it case insensitive
const keys = Object.keys(obj);
const wantedKey = keys.find(key => key.toLowerCase().includes(nameKey));
return wantedKey ? obj[wantedKey] : false;
}
console.log('Term "a" value:', search('a',data))
console.log('Term "bb" value:', search('bb',data))
console.log('Term "X" value:', search('X',data))
Since search criteria is vague I simply found any match anywhere in the property name and didn't look past the first one found

Efficient way to compare arrays in javascript

I need to compare the elements from two arrays as follows:
arr1[0] ? arr2[0]
arr1[1] ? arr2[1]
arr1[2] ? arr2[2]
etc.
I wrote some code but it seems to be slow when I try to compare 1000 objects like this on each array :
{
"id":"event707",
"name":"Custom707",
"type":"disabled",
"default_metric":false,
"participation":"disabled",
"serialization":"always_record"
}
This is how my function looks like (just an example for two arrays with hard coded data).
function compare() {
var step = 0;
var fruits1 = [{"apple":25},{"bannana":36},{"orange":6}];
var fruits2 = [{"apple":25},{"bannana":36},{"orange":6}];
for(var i=0;i<fruits1.length;i++) {
for(var j=step;j<fruits2.length;j++) {
console.log("FRUIT1");
console.log(JSON.stringify(fruits1[i]));
console.log("FRUIT2");
console.log(JSON.stringify(fruits2[j]));
console.log("----------------------");
if(JSON.stringify(fruits1[i])!== JSON.stringify(fruits2[j])) {
//do something
}
step = step + 1;
break;
}
}
}
With an invention of Object.prototype.compare() and Array.prototype.compare() this job becomes a very simple task. Array compare can handle both primitive and reference type items. Objects are compared shallow. Let's see how it works;
Object.prototype.compare = function(o){
var ok = Object.keys(this);
return typeof o === "object" && ok.length === Object.keys(o).length ? ok.every(k => this[k] === o[k]) : false;
};
Array.prototype.compare = function(a){
return this.every((e,i) => typeof a[i] === "object" ? a[i].compare(e) : a[i] === e);
};
var fruits1 = [{"apple":25},{"bannana":36},{"orange":6}],
fruits2 = [{"apple":25},{"bannana":36},{"orange":6}];
console.log(fruits1.compare(fruits2));
Simple function without library:
var arr1 = [1,2,3];
var arr2 = [1,2,4];
//This function takes one item, the index of the item, and another array to compare the item with.
function compare(item, index, array2){
return array2[index] == item;
}
// the forEach method gives the item as first parameter
// the index as second parameter
// and the array as third parameter. All are optional.
arr1.forEach(function(item, index){
console.log(compare(item, index, arr2));
});
Combine this with the answer Abdennour TOUMI gave, and you have an object comparison method :)
For simple objects you could use JSON.stringify(obj1) === JSON.stringify(obj2).
More info on object comparison can be found in this answer
Use underscore array functions. I would go with intersection
http://underscorejs.org/#intersection
You can use the following static method for Object class : Object.equals
Object.equals=function(a,b){if(a===b)return!0;if(!(a instanceof Object&&b instanceof Object))return!1;if(a.valueOf()===b.valueOf())return!0;if(a.constructor!==b.constructor)return!1;for(var c in a)if(a.hasOwnProperty(c)){if(!b.hasOwnProperty(c))return!1;if(a[c]!==b[c]){if("object"!=typeof a[c])return!1;if(!Object.equals(a[c],b[c]))return!1}}for(c in b)if(b.hasOwnProperty(c)&&!a.hasOwnProperty(c))return!1;return!0};
console.log(
`"[1,2,3] == [1,2,3]" ?`,Object.equals([1,2,3],[1,2,3])
);
console.log(
`"[{"apple":25},{"bannana":36},{"orange":6}] == [{"apple":25},{"bannana":36},{"orange":6}]" ?`,Object.equals([{"apple":25},{"bannana":36},{"orange":6}], [{"apple":25},{"bannana":36},{"orange":6}])
);

Comparing 2 JSON objects structure in JavaScript

I am using angular-translate for a big application. Having several people committing code + translations, many times the translation objects are not in sync.
I am building a Grunt plugin to look at both files' structure and compare it (just the keys and overall structure, not values).
The main goals are:
Look into each file, and check if the structure of the whole object
(or file, in this case) is the exact same as the translated ones;
On error, return the key that doesn't match.
It turns out it was a bit more complicated than I anticipated. So i figured I could do something like:
Sort the object;
Check the type of data the value contains (since they are translations, it will only have strings, or objects for the nestings) and store it in another object, making the key equal to the original key and the value would be a string 'String', or an object in case it's an object. That object contains the children elements;
Recursively repeat steps 1-2 until the whole object is mapped and sorted;
Do the same for all the files
Stringify and compare everything.
A tiny example would be the following object:
{
key1: 'cool',
key2: 'cooler',
keyWhatever: {
anotherObject: {
key1: 'better',
keyX: 'awesome'
},
aObject: 'actually, it\'s a string'
},
aKey: 'more awesomeness'
}
would map to:
{
aKey: 'String',
key1: 'String',
key2: 'String',
keyWhatever: {
aObject: 'String',
anotherObject: {
key1: 'String',
keyX: 'String'
}
}
}
After this, I would stringify all the objects and proceed with a strict comparison.
My question is, is there a better way to perform this? Both in terms of simplicity and performance, since there are many translation files and they are fairly big.
I tried to look for libraries that would already do this, but I couldn't find any.
Thank you
EDIT: Thank you Jared for pointing out objects can't be sorted. I am ashamed for saying something like that :D Another solution could be iterating each of the properties on the main translation file, and in case they are strings, compare the key with the other files. In case they are objects, "enter" them, and do the same. Maybe it is even simpler than my first guess. What should be done?
Lets say you have two JSON objects, jsonA and jsonB.
function compareValues(a, b) {
//if a and b aren't the same type, they can't be equal
if (typeof a !== typeof b) {
return false;
}
// Need the truthy guard because
// typeof null === 'object'
if (a && typeof a === 'object') {
var keysA = Object.keys(a).sort(),
keysB = Object.keys(b).sort();
//if a and b are objects with different no of keys, unequal
if (keysA.length !== keysB.length) {
return false;
}
//if keys aren't all the same, unequal
if (!keysA.every(function(k, i) { return k === keysB[i];})) {
return false;
}
//recurse on the values for each key
return keysA.every(function(key) {
//if we made it here, they have identical keys
return compareValues(a[key], b[key]);
});
//for primitives just use a straight up check
} else {
return a === b;
}
}
//true if their structure, values, and keys are identical
var passed = compareValues(jsonA, jsonB);
Note that this can overflow the stack for deeply nested JSON objects. Note also that this will work for JSON but not necessarily regular JS objects as special handling is needed for Date Objects, Regexes, etc.
Actually you do need to sort the keys, as they are not required to be spit out in any particular order. Write a function,
function getComparableForObject(obj) {
var keys = Object.keys(obj);
keys.sort(a, b => a > b ? 1 : -1);
var comparable = keys.map(
key => key + ":" + getValueRepresentation(obj[key])
).join(",");
return "{" + comparable + "}";
}
Where getValueRepresentation is a function that either returns "String" or calls getComparableForObject. If you are worried about circular references, add a Symbol to the outer scope, repr, assign obj[repr] = comparable in the function above, and in getValueRepresentation check every object for a defined obj[repr] and return it instead of processing it recursively.
Sorting an array of the keys from the object works. However, sorting has an average time complexity of O(nâ‹…log(n)). We can do better. A fast general algorithm for ensuring two sets A and B are equivalent is as follows:
for item in B
if item in A
remove item from A
else
sets are not equivalent
sets are equivalent iff A is empty
To address #Katana31, we can detect circular references as we go by maintaining a set of visited objects and ensuring that all descendents of that object are not already in the list:
# poorly written pseudo-code
fn detectCycles(A, found = {})
if A in found
there is a cycle
else
found = clone(found)
add A to found
for child in A
detectCycles(child, found)
Here's a complete implementation (you can find a simplified version that assumes JSON/non-circular input here):
var hasOwn = Object.prototype.hasOwnProperty;
var indexOf = Array.prototype.indexOf;
function isObjectEmpty(obj) {
for (var key in obj) {
return false;
}
return true;
}
function copyKeys(obj) {
var newObj = {};
for (var key in obj) {
newObj[key] = undefined;
}
return newObj;
}
// compares the structure of arbitrary values
function compareObjectStructure(a, b) {
return function innerCompare(a, b, pathA, pathB) {
if (typeof a !== typeof b) {
return false;
}
if (typeof a === 'object') {
// both or neither, but not mismatched
if (Array.isArray(a) !== Array.isArray(b)) {
return false;
}
if (indexOf.call(pathA, a) !== -1 || indexOf.call(pathB, b) !== -1) {
return false;
}
pathA = pathA.slice();
pathA.push(a);
pathB = pathB.slice();
pathB.push(b);
if (Array.isArray(a)) {
// can't compare structure in array if we don't have items in both
if (!a.length || !b.length) {
return true;
}
for (var i = 1; i < a.length; i++) {
if (!innerCompare(a[0], a[i], pathA, pathA)) {
return false;
}
}
for (var i = 0; i < b.length; i++) {
if (!innerCompare(a[0], b[i], pathA, pathB)) {
return false;
}
}
return true;
}
var map = copyKeys(a), keys = Object.keys(b);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!hasOwn.call(map, key) || !innerCompare(a[key], b[key], pathA,
pathB)) {
return false;
}
delete map[key];
}
// we should've found all the keys in the map
return isObjectEmpty(map);
}
return true;
}(a, b, [], []);
}
Note that this implementation directly compares two objects for structural equivalency, but doesn't reduce the objects to a directly comparable value (like a string). I haven't done any performance testing, but I suspect that it won't add significant value, though it will remove the need to repeatedly ensure objects are non-cyclic. For that reason, you could easily split compareObjectStructure into two functions - one to compare the structure and one to check for cycles.

Sort Array B after Array A, such that references and equal Primitives, maintain the exact Position

Update 2
I added a weight lookup to the sort function, which increased the performance by about 100% as well as the stability, as the previous sort function didn't consider all types, and as 1 == "1" the result depends on the initial order of the Array, as #Esailija points out.
The intent of the question is to improve this Answer of mine, I liked the question and since it got accepted and I felt like there is some performance to squeeze out of the sort function. I asked this question here since I hadn't many clues left where to start.
Maybe this makes things clearer as well
Update
I rephrased the complete question, as many people stated I was not specific enough, I did my best to specify what I mean. Also, I rewrote the sort function to better express the intent of the question.
Let arrayPrev be an Array (A) ,where A consists of 0 to n Elements' (E)
Let an Element either be
of a Primitive type
boolean, string, number, undefined, null
a Reference to an Object O, where O.type = [object Object] and O can consist of
0 to n Properties P, where P is defined like Element plus
an circular Reference to any P in O
Where any O can be contained 1 to n times. In the sense of GetReferencedName(E1) === GetReferencedName(E2)...
a Reference to an O, where O.type = [object Array] and O is defined like A
a circular Reference to any E in A
Let arrayCurr be an Array of the same length as arrayPrev
Illustrated in the following example
var x = {
a:"A Property",
x:"24th Property",
obj: {
a: "A Property"
},
prim : 1,
}
x.obj.circ = x;
var y = {
a:"A Property",
x:"24th Property",
obj: {
a: "A Property"
},
prim : 1,
}
y.obj.circ = y;
var z = {};
var a = [x,x,x,null,undefined,1,y,"2",x,z]
var b = [z,x,x,y,undefined,1,null,"2",x,x]
console.log (sort(a),sort(b,a))
The Question is, how can I efficiently sort an Array B, such that any Reference to an Object or value of a Primitive, shares the exact same Position as in a Previously, by the same compareFunction, sorted, Array A.
Like the above example
Where the resulting array shall fall under the rules.
Let arrayPrev contain the Elements' of a and arrayCurr contain the Elements' of b
Let arrayPrev be sorted by a CompareFunction C.
Let arrayCurr be sorted by the same C.
Let the result of sorting arrayCur be such, that when accessing an E in arrayCur at Position n, let n e.g be 5
if type of E is Object GetReferencedName(arrayCurr[n]) === GetReferencedName(arrayPrev[n])
if type of E is Primitive GetValue(arrayCurr[n]) === GetValue(arrayPrev[n])
i.e b[n] === a[n] e.g b[5] === a[5]
Meaning all Elements should be grouped by type, and in this sorted by value.
Where any call to a Function F in C shall be at least implemented before ES5, such that compatibility is given without the need of any shim.
My current approach is to Mark the Objects in arrayPrev to sort them accordingly in arrayCurr and later delete the Marker again. But that seems rather not that efficient.
Heres the current sort function used.
function sort3 (curr,prev) {
var weight = {
"[object Undefined]":6,
"[object Object]":5,
"[object Null]":4,
"[object String]":3,
"[object Number]":2,
"[object Boolean]":1
}
if (prev) { //mark the objects
for (var i = prev.length,j,t;i>0;i--) {
t = typeof (j = prev[i]);
if (j != null && t === "object") {
j._pos = i;
} else if (t !== "object" && t != "undefined" ) break;
}
}
curr.sort (sorter);
if (prev) {
for (var k = prev.length,l,t;k>0;k--) {
t = typeof (l = prev[k]);
if (t === "object" && l != null) {
delete l._pos;
} else if (t !== "object" && t != "undefined" ) break;
}
}
return curr;
function sorter (a,b) {
var tStr = Object.prototype.toString
var types = [tStr.call(a),tStr.call(b)]
var ret = [0,0];
if (types[0] === types[1] && types[0] === "[object Object]") {
if (prev) return a._pos - b._pos
else {
return a === b ? 0 : 1;
}
} else if (types [0] !== types [1]){
return weight[types[0]] - weight[types[1]]
}
return a>b?1:a<b?-1:0;
}
}
Heres a Fiddle as well as a JSPerf (feel free to add your snippet)
And the old Fiddle
If you know the arrays contain the same elements (with the same number of repetitions, possibly in a different order), then you can just copy the old array into the new one, like so:
function strangeSort(curr, prev) {
curr.length = 0; // delete the contents of curr
curr.push.apply(curr, prev); // append the contents of prev to curr
}
If you don't know the arrays contain the same elements, it doesn't make sense to do what you're asking.
Judging by the thing you linked, it's likely that you're trying to determine whether the arrays contain the same elements. In that case, the question you're asking isn't the question you mean to ask, and a sort-based approach may not be what you want at all. Instead, I recommend a count-based algorithm.
Compare the lengths of the arrays. If they're different, the arrays do not contain the same elements; return false. If the lengths are equal, continue.
Iterate through the first array and associate each element with a count of how many times you've seen it. Now that ES6 Maps exist, a Map is probably the best way to track the counts. If you don't use a Map, it may be necessary or convenient to maintain counts for items of different data types differently. (If you maintain counts for objects by giving them a new property, delete the new properties before you return.)
Iterate through the second array. For each element,
If no count is recorded for the element, return false.
If the count for the element is positive, decrease it by 1.
If the count for the element is 0, the element appears more times in the second array than in the first. Return false.
Return true.
If step 4 is reached, every item appears at least as many times in the first array as it does in the second. Otherwise, it would have been detected in step 3.1 or 3.3. If any item appeared more times in the first array than in the second, the first array would be bigger, and the algorithm would have returned in step 1. Thus, the arrays must contain the same elements with the same number of repetitions.
from the description, it appears you can simply use a sort function:
function sortOb(a,b){a=JSON.stringify(a);b=JSON.stringify(b); return a===b?0:(a>b?1:-1);}
var x = {a:1};
var y = {a:2};
var a = [1,x,2,3,y,4,x]
var b = [1,y,3,4,x,x,2]
a.sort(sortOb);
b.sort(sortOb);
console.log (a,b);
Your function always returns a copy of sort.el, but you can have that easier:
var sort = (function () {
var tmp;
function sorter(a, b) {
var types = [typeof a, typeof b];
if (types[0] === "object" && types[1] === "object") {
return tmp.indexOf(a) - tmp.indexOf(b); // sort by position in original
}
if (types[0] == "object") {
return 1;
}
if (types[1] == "object") {
return -1;
}
return a > b ? 1 : a < b ? -1 : 0;
}
return function (el) {
if (tmp) {
for (var i = 0; i < el.length; i++) {
el[i] = tmp[i]; // edit el in-place, same order as tmp
}
return el;
}
tmp = [].slice.call(el); // copy original
return tmp = el.sort(sorter); // cache result
};
})();
Note that I replaced sort.el by tmp and a closure. Fiddle: http://jsfiddle.net/VLSKK/
Edit: This (as well as your original solution)
only works when the arrays contain the same elements.
maintains the order of different objects in a
but
does not mess up when called more than twice
Try this
function ComplexSort (a, b)
{
if(typeof a == "object") {
a = a.a;
}
if(typeof b == "object") {
b = b.a;
}
return a-b;
}
var x = {a:1};
var y = {a:2};
var a = [1,x,2,3,y,4,x]
var b = [1,y,3,4,x,x,2]
a.sort(ComplexSort);
b.sort(ComplexSort);
console.log ("Consistent", a,b);

jQuery: Index of element in array where predicate

I have an array of objects. Each object has, among others, an ID attribute. I want to find the index in the array of the object with a specific ID. Is there any elegant and simple way to do this in jQuery?
See [`Array.filter`][1] to filter an array with a callback function. Each object in the array will be passed to the callback function one by one. The callback function must return `true` if the value is to be included, or false if not.
var matchingIDs = objects.filter(function(o) {
return o.ID == searchTerm;
});
All objects having the ID as searchTerm will be returned as an array to matchingIDs. Get the matching element from the first index (assuming ID is unique and there's only gonna be one)
matchingIDs[0];
[1]: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
Update:
Checkout findIndex from ECMAScript 6.
items.findIndex(function(item) { item.property == valueToSearch; });
Since findIndex isn't available on most browsers yet, you could backfill it using this implementation:
if (!Array.prototype.findIndex) {
Array.prototype.findIndex = function(predicate) {
if (this == null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
};
}
In the case you should use for loop in javascript instead of using jQuery. See way 3 in http://net.tutsplus.com/tutorials/javascript-ajax/10-ways-to-instantly-increase-your-jquery-performance/
UPDATED: jQuery is written in javascript and it can not be faster than another code written also in javascript. jQuery is very good if you work with the DOM, but doesn't really help if you're working with simple javascript arrays or objects.
The code you're looking for can be something like this:
for (var i=0, l = ar.length; i<l; i++) {
if (ar[i].ID === specificID) {
// i is the index. You can use it here directly or make a break
// and use i after the loop (variables in javascript declared
// in a block can be used anywhere in the same function)
break;
}
}
if (i<l) {
// i is the index
}
Important that you should hold some simple javascript rules: Always declare local variables (don't forget var before variable declaration) and cache any properties or indexes that you use more than one time in a local variable (like ar.length above). (See for example http://wiki.forum.nokia.com/index.php/JavaScript_Performance_Best_Practices)
Not really elegant, but a cute trick:
var index = parseInt(
$.map(array, function(i, o) { return o.id === target ? i : ''; }).join('')
);
jQuery doesn't have a lot of functional constructs like that; the philosophy of the library is really focused on the job of DOM wrangling. They won't even add a .reduce() function because nobody can think of a reason it'd be useful to the core functionality.
The Underscore.js library has a lot of such facilities, and it "plays nice" with jQuery.
There are no built-in methods for this; the [].indexOf() method doesn't take a predicate, so you need something custom:
function indexOf(array, predicate)
{
for (var i = 0, n = array.length; i != n; ++i) {
if (predicate(array[i])) {
return i;
}
}
return -1;
}
var index = indexOf(arr, function(item) {
return item.ID == 'foo';
});
The function returns -1 if the predicate never yields a truthy value.
Update
There's Array.findIndex() that you could use now:
const arr = [{ID: 'bar'}, {ID: 'baz'}, {ID: 'foo'}];
const index = arr.findIndex(item => item.ID === 'foo');
console.log(index); // 2
Use jOrder. http://github.com/danstocker/jorder
Feed your array into a jOrder table, and add an index on the 'ID' field.
var table = jOrder(data)
.index('id', ['ID']);
Then, get the array index of an element by:
var arrayidx = table.index('id').lookup([{ ID: MyID }]);
If you want the entire row, then:
var filtered = table.where([{ ID: MyID }]);
Voila.

Categories