Code works with arrays, but not multidimensional arrays. - javascript

Working on a Holdem hand evaluator, and part of the yak shaving is writing a "how many combos of 5 do you get from 7 cards" function (pickNofSet()). I've done that, but the way I've done that returns a bunch of duplicates.
So I have to write a removeDuplicates(). Here's the problem... it works with a simple array, but it doesn't work with the "arrays of arrays" that my "pickNofSet" function generates.
-- here's the removeDuplicates code --
var removeDuplicates = function(input){ // takes array
var output = [];
for (i=0; i < input.length; i++){
var unique = true; // all elements are innocent until proven guilty
for(j=i+1; j < input.length; j++){
if(input[j] === input[i]){
unique = false; // guilty!
};// endif
};// end jfor
if(unique){ // if not found guilty,
output.push(input[i]); // you may go free, little element
};// end if
};// end ifor
console.log(output);
return output; };//end function
Here's what I get from the Console:
> removeDuplicates(['a','b','c'],['a','b','c'],['d','e','f'],['g','h','i']);
< undefined
> removeDuplicates([1, 2, 2, 3, 3, 5, 5, 6, 6, 6]);
< [1, 2, 3, 5, 6]

Operator === can't compare array
As you use === to check equality between two elements, this only works with primitive data types, but not array. e.g.
1===1 // true
[1]===[1] // Sorry, you can't
If you want your algorithm to work with both primitive elements and array elements, you may need to upgrade your equality check from === to a customized function.
From this:
if(input[j] === input[i]){
unique = false; // guilty!
};// endif
To this:
if (equals(input[j],input[i]){
unique = false; // guilty!
};// endif
And implement equals function to be capable of comparing both primitive data types and arrays:
function equals(a,b){
if (typeof(a)!=typeof(b))
return false;
else if (typeof(a)=='object'){
if (Object.keys(a).length != Object.keys(b).length)
return false;
else{
for (var keyA of Object.keys(a)){
if (!(keyA in b))
return false;
else if (a[keyA]!==b[keyA])
return false;
else
return true;
}
}
}
else
return a===b;
}
Hint: This solution should, hopefully, also work with JSON element.

Here is an example of a general purpose unique filter that should fill your need. Requires an ES5 compliant environment.
(function () {
'use strict';
function $strictEqual(a, b) {
return a === b;
}
function $isUndefined(inputArg) {
return $strictEqual(typeof inputArg, 'undefined');
}
function $isPrimitive(inputArg) {
var type = typeof inputArg;
return type === 'undefined' || inputArg === null || type === 'boolean' || type === 'string' || type === 'number' || type === 'symbol';
}
function $isFunction(inputArg) {
return typeof inputArg === 'function';
}
function $isDate(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object Date]';
}
function $isRegExp(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object RegExp]';
}
function $isString(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object String]';
}
function $isArguments(inputArg) {
return Object.prototype.toString.call(inputArg) === '[object Arguments]';
}
function $getItem(object, index) {
var item;
if ($isString(object)) {
item = object.charAt(index);
} else {
item = object[index];
}
return item;
}
var de = function (a, b, circ) {
if (a === b) {
return true;
}
var aType,
bType,
aIsArgs,
bIsArgs,
aIsPrim,
bIsPrim,
aCirc,
bCirc,
ka,
kb,
length,
index,
it;
if ($isDate(a) && $isDate(b)) {
return a.getTime() === b.getTime();
}
if ($isRegExp(a) && $isRegExp(b)) {
return a.source === b.source &&
a.global === b.global &&
a.multiline === b.multiline &&
a.lastIndex === b.lastIndex &&
a.ignoreCase === b.ignoreCase &&
a.sticky === b.sticky;
}
aIsPrim = $isPrimitive(a);
bIsPrim = $isPrimitive(b);
if ((aIsPrim || $isFunction(a)) && (bIsPrim || $isFunction(b))) {
/*jslint eqeq: true */
return a == b;
}
if (aIsPrim || bIsPrim) {
return a === b;
}
if (a.prototype !== b.prototype) {
return false;
}
if (circ.a.indexOf(a) === -1) {
circ.a.push(a);
} else {
aCirc = true;
}
if (circ.b.indexOf(b) === -1) {
circ.b.push(b);
} else {
bCirc = true;
}
if (aCirc && bCirc) {
circ.cnt += 1;
} else {
circ.cnt = 0;
}
if (circ.cnt > 200) {
throw new RangeError('Circular reference limit exceeded');
}
aIsArgs = $isArguments(a);
bIsArgs = $isArguments(b);
if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) {
return false;
}
if (aIsArgs) {
return de(Array.prototype.slice.call(a), Array.prototype.slice.call(b), circ);
}
ka = Object.keys(a);
kb = Object.keys(b);
length = ka.length;
if (length !== kb.length) {
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) {
return false;
}
} else {
return false;
}
} else {
ka.sort();
kb.sort();
for (index = 0; index < length; index += 1) {
if (ka[index] !== kb[index]) {
return false;
}
}
}
for (index = 0; index < length; index += 1) {
it = ka[index];
if (!de($getItem(a, it), $getItem(b, it), circ)) {
return false;
}
}
aType = typeof a;
bType = typeof b;
return aType === bType;
};
if (!Object.prototype.deepEqual) {
Object.defineProperty(Object.prototype, 'deepEqual', {
enumerable: false,
configurable: true,
writable: true,
value: function (b) {
var a = this;
return de(a, b, {
a: [],
b: [],
cnt: 0
});
}
});
}
if (!Array.prototype.unique) {
Object.defineProperty(Array.prototype, 'unique', {
enumerable: false,
configurable: true,
writable: true,
value: function (equalFn, thisArg) {
var object = Object(this),
length,
index,
eqFn,
arr,
idx,
val,
it;
if ($isUndefined(equalFn)) {
eqFn = $strictEqual;
} else {
eqFn = equalFn;
}
if (!$isFunction(eqFn)) {
throw new TypeError('Argument is not a function: ' + eqFn);
}
arr = [];
length = object.length >>> 0;
for (index = 0; index < length; index += 1) {
if (index in object) {
it = $getItem(object, index);
val = true;
for (idx = 0; idx < length; idx += 1) {
if (idx < index && idx in object && eqFn.call(thisArg, it, $getItem(object, idx))) {
val = false;
break;
}
}
if (val) {
arr.push(it);
}
}
}
return arr;
}
});
}
}());
var data1 = [1, 2, 2, 3, 3, 5, 5, 6, 6, 6],
data2 = [
['a', 'b', 'c'],
['a', 'b', 'c'],
['d', 'e', 'f'],
['g', 'h', 'i']
],
equals = Function.prototype.call.bind(Object.prototype.deepEqual),
pre = document.getElementById('out');
pre.textContent = JSON.stringify(data1.unique(equals), null, 2);
pre.textContent += '\n\n';
pre.textContent += JSON.stringify(data2.unique(equals), null, 2);
<pre id="out"></pre>

Related

The function set fails to update a non existant path under an existing key

See online playground link: https://repl.it/repls/ChocolateHummingMacroinstruction
I have a function that sets a deep nested property:
const rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
const reIsUint = /^(?:0|[1-9]\d*)$/;
function isIndex(value, length = Number.POSITIVE_INFINITY) {
const isNumberLike = (value) => typeof value == 'number' || (typeof value != 'symbol' && reIsUint.test(value));
return isNumberLike(value) && (value > -1 && value % 1 == 0 && value < length);
}
function stringToPath (string) {
const result = [];
if (string.charCodeAt(0) === 46 /* . */) {
result.push('');
}
string.replace(rePropName, function(match, number, quote, subString) {
result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
});
return result;
}
function set(object, path, value) {
path = stringToPath(path);
const length = path.length;
const lastIndex = length - 1;
let index = -1;
let nested = object;
while (nested && ++index < length) {
const key = path[index];
let newValue = value;
if (index !== lastIndex) {
const objValue = nested[key];
if (value != null && typeof value == 'object') {
newValue = objValue;
} else if (isIndex(path[index + 1])) {
newValue = [];
} else {
newValue = {};
}
}
const isNotToBeUpdated = (object, key, value) => object.hasOwnProperty(key) &&
object[key] === value ||
(value === undefined && !(key in object));
if (!isNotToBeUpdated(nested, key, newValue)) {
nested[key] = newValue;
}
nested = nested[key];
}
return object;
}
Given that I have an object that looks like this:
const obj = {
the: {
dog: {
and: {
fox: 123
}
}
}
};
set(obj, 'the.war.of.wolf', 456);
It clobbers the existing value of the key and replaces it with the new value:
{
the: {
war: {
of: {
wolf: 456
}
}
}
}
Instead, I would like for it to update the nested property without destroying the existing value:
{
the: {
dog: {
and: {
fox: 123
}
},
war: {
of: {
wolf: 456
}
}
}
}

Safari sort() function doesnt work

Why does my code not work correctly in the Safari browser on Iphone 6 ios 10 and above. In the support table, all values used in the code are supported! The code works everywhere except this device. For my data i use Wilddog it same work as Firebase. Array look like {name: 'Some', like: 2, fav_count: 5 }, {name: 'Some2', like: 3, fav_count: 4 }, {name: 'Some3', like: 3, fav_count: 4 }. Something wrong going on sort() function;
var obj = $wilddogObject(ref); // get data from server
obj.$loaded()
.then(function(data) {
var _extends = Object.assign || function(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } }
return target; };
var moments = _.map(data, function(val, id) {
return _extends({}, val, {
id: id
});
});
$scope.ratePlaces = _.map(moments, function(place) {
// console.log(place);
if (place != null) {
if (place.like != undefined) {
function rating(like, favs) {
var parseFavs = parseInt(favs);
var parseLike = parseInt(like);
var newRate = parseLike * 0.5 + parseFavs;
return newRate;
}
place.rate = rating(place.like, place.fav_count);
}
}
return place
})
console.log($scope.ratePlaces);
list($scope.ratePlaces)
})
function list(r) {
r.shift();
r.shift();
r.shift();
r.sort((a, b) => {
if (a.rate != null || a.rate != undefined || a.rate != 'daylife' || !a.name) {
return a.rate - b.rate;
} else {}
})
r.sort((a, b) => {
if (a.rate != null || a.rate != undefined || a.rate != 'daylife' || !a.name) {
let rateA = a.rate;
let rateB = b.rate;
if (rateA < rateB) {
return -1;
}
if (rateA > rateB) {
return 1;
}
return 0;
}
});
$scope.posts = r.reverse();

traversing a json for empty array value

i have a below json
{
"loanDetails": [
{
"vehicleDetail": {
"RCBookImageReferences": {
"imagePathReferences": [
{
}
]
}
},
"chargeDetails": [
{
}
],
"commissionDetails": [
{
}
],
"disbursementDetails": [
{
}
]
}
]
}
in the above json i need to traverse every key and if i find it emty then set the parent as empty array ie the output should be as below
{"loanDetails":[]}
i used the code below
function isEmpty(obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
return false;
}
return true;
}
But it did not give me the expected result.I'm stuck here any help will be much helpful.
The function clean takes an object and loops over its keys, calling clean recursively
on each object-valued property.
If the result of cleaning is an empty object, delete the key in question.
If the object itself turns out to be empty, return undefined, triggering deletion of the property holding that object at the higher level.
function clean(obj) {
var isEmpty = true;
for (var key in obj) {
var val = obj[key];
if (val === null || typeof val !== 'object' || (obj[key] = clean(val))) {
isEmpty = false;
} else {
delete obj[key];
}
}
return isEmpty ? undefined : obj;
}
>> a = { x: 1, b: { y: [] }, c: { d: { } } }
>> clean(a)
<< Object {x: 1}
This should make it recursive. With two solutions.
Solution 1: empty test function
var boolValue = true;
for(var prop in obj) {
if(obj.hasOwnProperty(prop) && typeof obj[prop] === 'object')
{
boolValue = recursiveIsEmpty(obj[prop]);
}
else
{
return false;
}
}
return boolValue ;
//test and set empty string
recursiveIsEmpty(jsonDataObj['loanDetails']) ? jsonDataObj['loanDetails'] = [] : null;
Solution 2 recursive empty function that empties parent obj
function recursiveIsEmpty(obj) {
var boolValue = true;
for(var prop in obj) {
if(obj.hasOwnProperty(prop) && typeof obj[prop] === 'object')
{
boolValue = recursiveIsEmpty(obj[prop]);
if (boolValue)
{
delete obj[prop]; //an object is empty. Delete from parent;
}
}
else
{
return false;
}
}
return boolValue; //returns an empty object
}
recursiveIsEmpty(jsonDataObj['loanDetails']) //returns jsonDataObj['loanDetails'] = [];
This checks if obj has a property that is an object. If so load that object and check it's properties. If not return false, because that will be string or number and that confirms the object is not empty.
Your JSON-string is not valid. When corrected, you can use a reviver function parameter (see MDN) to remove 'empty' arrays (aka properties with criteria you specify).
To be clear, the reviver function takes care of the traversing on all levels of the parsed object. If it returns undefined the property is removed from the object. The reviver used in the snippet thus removes all properties containing arrays with empty objects, or empty arrays.
The snippet demonstrates this.
// json string corrected
var foo = '{"loanDetails": [{"vehicleDetail": {"RCBookImageReferences": '+
'{"imagePathReferences": [{}]}}, "chargeDetails": [{}],'+
'"commissionDetails": [{}],"disbursementDetails": [{}]}]}';
// parse it, using reviver parameter
var fooparsed = JSON.parse( foo,
function (key, value) { //<= reviver here
return (value.length && value.length == 1 &&
value[0] instanceof Object &&
Object.keys(value[0]).length == 0) ||
value instanceof Array && !value.length
? undefined : value;
}
);
// print
Helpers.log2Screen( Object.print(fooparsed) );
<script src="http://kooiinc.github.io/JSHelpers/Helpers-min.js"></script>
if you are doing this using ajax then you should go with seriallizing the jason array using javascript.
at the time of passing data through json
data: "your data",
use this
data:$(form).serialize(),
it will pass all the key of that form which you are passing ,
if you want to see its result the try to print it on console
var inputObj = {
"loanDetails": [{
"vehicleDetail": {
"RCBookImageReferences": {
"imagePathReferences": [{}]
}
},
"chargeDetails": [{}],
"commissionDetails": [{}],
"disbursementDetails": [{}]
}, {
"vehicleDetail": {
"RCBookImageReferences": {
"imagePathReferences": [{
"Valid": "Working"
}]
}
},
"chargeDetails": [{}],
"commissionDetails": [{}],
"disbursementDetails": [{}]
}],
"Superman": {
"Name": ""
},
"SpiderMan": {
"Name": "Senthil"
}
}
function flatten(target, opts) {
var output = {},
opts = opts || {},
delimiter = opts.delimiter || '.'
function getkey(key, prev) {
return prev ? prev + delimiter + key : key
};
function step(object, prev) {
Object.keys(object).forEach(function(key) {
var isarray = opts.safe && Array.isArray(object[key]),
type = Object.prototype.toString.call(object[key]),
isobject = (type === "[object Object]" || type === "[object Array]")
if (!isarray && isobject) {
return step(object[key], getkey(key, prev))
}
output[getkey(key, prev)] = object[key]
});
if (Object.keys(object) == "") {
if (object instanceof Array) {
output[prev] = [];
} else {
output[prev] = {};
}
}
};
step(target)
return output
};
function unflatten(target, opts) {
var opts = opts || {},
delimiter = opts.delimiter || '.',
result = {}
if (Object.prototype.toString.call(target) !== '[object Object]') {
return target
}
function getkey(key) {
var parsedKey = parseInt(key)
return (isNaN(parsedKey) ? key : parsedKey)
};
Object.keys(target).forEach(function(key) {
var split = key.split(delimiter),
firstNibble, secondNibble, recipient = result
firstNibble = getkey(split.shift())
secondNibble = getkey(split[0])
while (secondNibble !== undefined) {
if (recipient[firstNibble] === undefined) {
recipient[firstNibble] = ((typeof secondNibble === 'number') ? [] : {})
}
recipient = recipient[firstNibble]
if (split.length > 0) {
firstNibble = getkey(split.shift())
secondNibble = getkey(split[0])
}
}
// unflatten again for 'messy objects'
recipient[firstNibble] = unflatten(target[key])
});
//Array Check
var keys = Object.keys(result);
if (keys.length > 0 && keys[0] === "0") {
var output = [];
keys.forEach(function(key) {
output.push(result[key])
});
return output;
}
return result
};
var flatted = flatten(inputObj);
var keys = Object.keys(flatted);
keys.forEach(function(key) {
if (JSON.stringify(flatted[key]) === "{}" || JSON.stringify(flatted[key]) == "") {
// console.log(key)
delete flatted[key];
var paths = key.split(".");
if (paths.length >= 2) {
var int = parseInt(paths[1])
if (isNaN(int)) {
key = paths[0];
flatted[key] = {};
} else {
key = paths[0] + "." + int;
flatted[key] = {};
}
var newKeys = Object.keys(flatted);
for (var j = 0; j < newKeys.length; j++) {
var omg = newKeys[j];
if (omg.indexOf(key) != -1 && omg.length > key.length) {
delete flatted[key];
}
}
}
}
})
console.log(flatted)
var output = unflatten(flatted);
alert(JSON.stringify(output))

Easy way to get multi-level javascript object property

I always need to deal with multi-level js objects where existence of properties are not certain:
try { value1 = obj.a.b.c; } catch(e) { value1 = 1; }
try { value2 = obj.d.e.f; } catch(e) { value2 = 2; }
......
Is there an easier way or a generic function (e.g. ifnull(obj.d.e.f, 2) ) that does not require a lot of try catches?
var value1 = (obj.a && obj.a.b && obj.a.b.c) || 1;
http://jsfiddle.net/DerekL/UfJEQ/
Or use this:
function ifNull(obj, key, defVal){
var keys = key.split("."), value;
for(var i = 0; i < keys.length; i++){
if(typeof obj[keys[i]] !== "undefined"){
value = obj = obj[keys[i]];
}else{
return defVal;
}
}
return value;
}
var value1 = ifNull(obj, "a.b.c", 1);
You could always create a helper function.
function isUndefined(root, path, defaultVal) {
var parts = path.split('.'),
i = 0,
len = parts.length,
o = root || {}, v;
while ((typeof (v = o[parts[i]]) === 'object', o = v) && ++i < len);
return (typeof v === 'undefined' || i !== len)? defaultVal: v;
}
var obj = {a: { b: { test: 'test' }}}, v;
v = isUndefined(obj, 'a.b.test', 1); //test
v = isUndefined(obj, 'a.test', 1); //1
Using lodash you can do this easily**(node exists and empty check for that node)**..
var lodash = require('lodash-contrib');
function invalidateRequest(obj, param) {
var valid = true;
param.forEach(function(val) {
if(!lodash.hasPath(obj, val)) {
valid = false;
} else {
if(lodash.getPath(obj, val) == null || lodash.getPath(obj, val) == undefined || lodash.getPath(obj, val) == '') {
valid = false;
}
}
});
return valid;
}
Usage:
leaveDetails = {
"startDay": 1414998000000,
"endDay": 1415084400000,
"test": { "test1" : 1234 }
};
var validate;
validate = invalidateRequest(leaveDetails, ['startDay', 'endDay', 'test.test1']);
it will return boolean.

javascript comparing arrays of objects

I'm getting a "Maximum call stack size exceeded." while trying to iterate the contents of an array and I can't for the life of me figure out why
Edit: By the way I want to add that this is for Safari
here's the function:
function compareObj(a,b) {
var u = function(c,d) {//Check types
var type = Object.prototype.toString.call(a)
if (type !== Object.prototype.toString.call(b)) return false
return type.slice(8,-1) == 'Array' ? v(a,b) : type.slice(8,-1) == 'Object' ? w(a,b) :
type.slice(8,-1) == 'String' ? x(a,b) : type.slice(8,-1) == 'Number' ? y(a,b) :
type.slice(8,-1) == 'Boolean' ? z(a,b) : false ;
}
var v = function(c,d) {//Array
if (c.length !== d.length) return false
/*for (var i = 0; i < c.length; i++) {
if (!u(c[i],d[i])) {
return false
}
}*/
while (c.length) {
if (!u(c[0],d[0])) {
return false
} else {
c.shift();
d.shift();
}
}
return true
}
var w = function(c,d) {//Object
for (i in c) {
}
return true
}
var x = function(c,d) {//String
return c === d
}
var y = function(c,d) {//Number
return c === d
}
var z = function(c,d) {//Boolean
return c === d
}
return u(a,b)
}
I get the overload in the return statement of the u() function
Your function can be a lot shorter. It's advisable to use meaningfull function names. Some functions are redundant. There is no need for recursion. I took the liberty to rewrite your function, just for demonstration. You can see it in action here.
function compareObj(a,b) {
var doCompare = function(c,d) {
//Check types (IE compatible)
var objRE = /Array|Object|String|Number|Boolean/,
aType = a.constructor.name || String(a.constructor).match(objRE)[0],
bType = b.constructor.name || String(b.constructor).match(objRE)[0];
//Types different? No dice.
if (aType !== bType) {return false;}
switch(aType){
case 'Array' : return arrayCompare(a,b); break;
case 'Object' : return objectCompare(a,b); break;
case 'String' : return valueCompare(a,b); break;
case 'Number' : return valueCompare(a,b); break;
case 'Boolean': return valueCompare(a,b); break;
default: return false
}
}
var arrayCompare = function(c,d) { //Array
if (c.length !== d.length) {return false;}
var i = c.length;
while (--i) {
if ( !valueCompare(c[i],d[i]) ) {
return false;
}
}
return true;
}
var objectCompare = function(c,d) { //Object
//you'll have to consider the number of elements too
var lenb = 0, lena = 0;
for (var label in d){
if (d.hasOwnProperty(label)) { lenb+=1; }
}
for (var label in c) {
if (c.hasOwnProperty(label)){
lena += 1;
if ( !valueCompare(c[label],d[label]) ) {
return false;
}
}
}
return lena === lenb;
}
var valueCompare = function(c,d) {//String
return JSON.stringify(c) === JSON.stringify(d);
}
return doCompare(a,b);
}
Actually, if you use JSON (native in newer browsers, or from library), you can do all this just using:
function compare(a,b){
return JSON.stringify(a) === JSON.stringify(b);
}

Categories