To find object whereabouts in array of objects - javascript

function whatIsInAName(collection, source) {
var result = [];
var arr1 = Object.keys(source);
console.log(arr1);
for (var i = 0; i < collection.length; i++) {
for (var j = 0; j < arr1.length; j++) {
if (collection[i].hasOwnProperty(arr1[j]) === false) { //Check 1 if false go to next object in collection
break;
} else if (collection[i].hasOwnProperty(arr1[j])) {
console.log(source[arr1[j]], collection[i][arr1[j]])
if (source[arr1[j]] !== collection[i][arr1[j]]) { //Check 2 if value is not equal break loop and goto next object in collection
break;
}
continue; // if both check passes go for next property of source to check in object;
}
result.push(collection[i]); //if all values are present and checked in object push it in result array.
}
}
return result;
}
console.log(whatIsInAName(
[
{ a: 1, b: 2 },
{ a: 1 },
{ a: 1, b: 2, c: 2 }
], {
a: 1,
b: 2
}
));
I couldn't figure out the problem in my logic. I try to debug it even but can't find what the hell is a problem with logic.The program is to make a function that looks through an array of objects (first argument) and returns an array of all objects that have matching property and value pairs (second argument) Kindly help me over, please.
whatIsInAName([{ "a": 1, "b": 2 }, { "a": 1 }, { "a": 1, "b": 2, "c": 2 }], { "a": 1, "b": 2 })
should return
[{ "a": 1, "b": 2 }, { "a": 1, "b": 2, "c": 2 }].
and
whatIsInAName([{ "a": 1 }, { "a": 1 }, { "a": 1, "b": 2 }], { "a": 1 })
should return
[{ "a": 1 }, { "a": 1 }, { "a": 1, "b": 2 }].

Well you are complicating it too much with these two for loops, you can do it better using Array built-in methods.
This is how you can do it using .filter() and .some() methods:
function whatIsInAName(collection, source) {
var result = [];
var arr1 = Object.keys(source);
console.log(arr1);
result = collection.filter(function(obj){
return !arr1.some(function(k){
return !obj[k] || obj[k] !== source[k];
});
});
return result;
}
console.log(whatIsInAName([{ a: 1, b: 2 }, { a: 1 }, { a: 1, b: 2, c: 2 }], { a: 1, b: 2 }));

Problem with your code :
What you are doing currently is that if all the if statements pass, and loops do not break anywhere, you put continue again at the last item of arr1. So, it goes to check next iteration, does not find it, and goes to next iteration in the collection loop. While doing this, it does not push any item to result.
Solution:
In your code: You should use continue like this.
if(j !==(arr1.length - 1))
continue;
This gives your code an opportunity to push item to result array.

Try this function
function whatIsInAName(arr, sources){
return arr.filter((item) => {
for(source in sources){
if(!item[source] || item[source] !== sources[source]){
return false;
}
}
return true;
})
}

I think you are over complicating things. A simple .filter() will do. Inside that .filter() you can check to see if every value for every key in source matches with the corresponding key in collection by using the handy, built-in .every() method.
function whatIsInAName(collection, source) {
var sourceKeys = Object.keys(source);
return collection.filter(function (coll) {
// if you want to return only exact matches, just add the test to make sure same # of keys, and all keys match w/ values.
// (Object.keys(coll).length === sourceKeys.length) && sourceKeys.every(...)
return sourceKeys.every(function (key) {
return coll[key] === source[key];
});
});
}
console.log(whatIsInAName(
[
{ a: 1, b: 2 },
{ a: 1 },
{ a: 1, b: 2, c: 2 }
], {
a: 1,
b: 2
}
));
Alternatively, with ES6 Syntax:
function whatIsInAName(collection, source) {
return collection.filter(coll => Object.keys(source).every(key => coll[key] === source[key]));
}
console.log(whatIsInAName(
[
{ a: 1, b: 2 },
{ a: 1 },
{ a: 1, b: 2, c: 2 }
], {
a: 1,
b: 2
}
));

Your issue is that you are hitting your continue instead of falling out of the loop. I updated your code here:
function whatIsInAName(collection, source) {
var result = [];
var arr1 = Object.keys(source);
console.log(arr1);
for (var i = 0; i < collection.length; i++) {
for (var j = 0; j < arr1.length; j++) {
if (!collection[i].hasOwnProperty(arr1[j])) { //Check 1 if false go to next object in collection
break;
} else if (collection[i].hasOwnProperty(arr1[j])) {
console.log(source[arr1[j]], collection[i][arr1[j]])
if (source[arr1[j]] !== collection[i][arr1[j]]) { //Check 2 if value is not equal break loop and goto next object in collection
break;
}else if(j < arr1.length - 1){
continue; // if both check passes go for next property of source to check in object;
}
}
result.push(collection[i]);
}
}
return result;
}
console.log(whatIsInAName(
[
{ a: 1, b: 2 },
{ a: 1 },
{ a: 1, b: 2, c: 2 }
],
{
a: 1,
b: 2
}
)); // result is [Object {...}, Object {...}] which objects are {a: 1, b: 2}, {a: 1, b: 2, c: 2}

Related

javascript : How to check json contains another json

Want to verify all list from referjson should be present in response json (response).
referjson = [
{
a: 1,
b: 2,
c: 4,
tag: "test"
},
{
a: 3,
b: 5,
tag: "mock"
},
...
];
response = [
{
tag: "mock",
a: 3,
b: 5,
c: 0,
d: 0,
e: 0
},
{
tag: "test",
a: 1,
b: 2,
c: 4,
d: 0,
e: 0
},
{
tag: "mocktest",
a: 3,
b: 5,
c: 0,
d: 0,
e: 0
},
...
];
Kindly help me to check one by one all the list ie { a: 1, b: 2, c: 4, tag: "test" } should be present in response then check for {a: 3, b: 5, tag: "mock" } and so on ..
function checkJSON(referjson, response) {
for (var i = 0; i < referjson.length; i++) {
if (response.contains(referjson[i])) {
print("**PASS");
}
else {
karate.log(x[i] + "-------------Fail");
}
}
}
My if doesn't do it for me.
I came with something like this...
const checkJson = (referjson, response) => {
//will have all the keys of obj so we can compare with response has all the keys too.
let keyValueIsEqual = [];
let flag;
let indexResponse = 0;
let indexReferjson = 0;
let numberOfEqualObj = 0;
while(indexResponse < response.length && indexReferjson < referjson.length){
const referjsonEntries = Object.entries(referjson[indexReferjson]);
const responseEntries = Object.entries(response[indexResponse]);
if(referjsonEntries.length === responseEntries.length) {
for (const [key, value] of referjsonEntries) {
flag = false;
for(const [keyRes, valueRes] of responseEntries) {
if(keyRes === key && value === valueRes) {
keyValueIsEqual.push(true);
flag = true;
}
}
if(!flag) {
indexResponse++;
keyValueIsEqual = [];
break;
}
if(keyValueIsEqual.length === referjsonEntries.length) {
//response has all the keys and values [key, value] in one object -> meaning that they have equal objects.
indexReferjson++;
indexResponse = 0;
keyValueIsEqual = [];
numberOfEqualObj++;
}
}
} else {
indexResponse++;
keyValueIsEqual = [];
}
}
if (numberOfEqualObj === referjson.length) {
//response have all objs that referjson contain
} else {
//response doesn't have all the objs that referjson contain
}
}
In your example, "referjson" and "response" are not actually jsons, they're an array of objects, so we can iterate over their's entries...
I check if "referjsonEntries.length" is equal to "responseEntries.length" because the object will only be equal if they have the same quantity of properties.
I created an array "keyValueisEqual" that will store "true" in case the object we're iterating over in "response" has the key and value equals to the object in "referjson". Created a flag that indicates that the object of "reponseEntries" has the key and value equals to the object of "referjsonEntries". So now we only need to check if "keyValueIsEqual" has same length of "referjsonEntries", wich means that response's object is equal to referjson's object, increase the number of "numberOfEqualObj" and in the end of the code I check if "numberOfEqualObj" has the same length of "referjson", wich means that "response" has at least all the objects that "referjson" has. Don't think it's the optimized solution, but it works.

Unflatten JS object and convert arrays

I have a function used to flatten objects like so:
let object = {
a: 1,
b: [
{ c: 2 },
{ c: 3 }
]
};
flatten(object)
// returns {
'a': 1,
'b.0.c': 2,
'b.1.c': 3
}
I need to unflatten objects, but also revert arrays to how they were. I have the following code:
unflatten(obj) {
let final = {};
for (let prop in obj) {
this.assign(final, prop.split('.'), obj[prop]);
}
return final;
}
assign(final, path, value) {
let lastKeyIndex = path.length-1;
for (var i = 0; i < lastKeyIndex; ++ i) {
let key = path[i];
if (!(key in final)) {
final[key] = {};
}
final = final[key];
}
final[path[lastKeyIndex]] = value;
}
which works for the most part, but it treats arrays like so:
{
a: 1,
b: { // Notice how b's value is now an object
"0": { c: 2 }, // Notice how these now have a key corresponding to their index
"1": { c: 3 }
}
}
Whereas I need b to be an array like before:
{
a: 1,
b: [
{ c: 2 },
{ c: 3 }
]
}
I'm at a loss for where to go from here. It needs to be able to deal with an arbitrary number of arrays like:
'a.b.0.c.0.d',
'a.b.0.c.1.d',
'a.b.1.c.0.d',
'a.b.1.c.1.d',
'a.b.1.c.2.d',
// etc
It needs to be vanilla JS, but es2015 is fine. It it assumed that any key that's a number is actually part of an array.
If anyone has any advice, it's appreciated!
When you find that key is not in final, you should check to see if the next key in the path is only digits (using a regular expression) and, if so, assign to an array instead of an object:
if (!(key in final)) {
final[key] = /^\d+$/.test(path[i + 1]) ? [] : {};
}
let object = {
a: 1,
b: [{
c: 2
},
{
c: 3
}
]
};
let flattened = {
'a': 1,
'b.0.c': 2,
'b.1.c': 3
}
function unflatten(obj) {
let final = {};
for (let prop in obj) {
assign(final, prop.split('.'), obj[prop]);
}
return final;
}
function assign (final, path, value) {
let lastKeyIndex = path.length - 1;
for (var i = 0; i < lastKeyIndex; ++i) {
let key = path[i];
if (!(key in final)) {
final[key] = /^\d+$/.test(path[i + 1]) ? [] : {};
}
final = final[key];
}
final[path[lastKeyIndex]] = value;
}
console.log(unflatten(flattened))
.as-console-wrapper { min-height: 100vh; }
You could iterate the keys and then split the string for single properties. For building a new object, you could check for number and take an array for these properties.
function setValue(object, path, value) {
var way = path.split('.'),
last = way.pop();
way.reduce(function (o, k, i, kk) {
return o[k] = o[k] || (isFinite(i + 1 in kk ? kk[i + 1] : last) ? [] : {});
}, object)[last] = value;
}
function unFlatten(object) {
var keys = Object.keys(object),
result = isFinite(keys[0][0]) ? [] : {};
keys.forEach(function (k) {
setValue(result, k, object[k]);
});
return result;
}
console.log(unFlatten({
'a': 1,
'b.0.c': 2,
'b.1.c': 3
}));
console.log(unFlatten({
'0': 1,
'1.0.c': 2,
'1.1.c': 3
}));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Compare 2 objects and remove repeating keys between

I am experimenting on objects, and what I am trying to achieve is to remove keys found in object1 if those keys exist in object2.
Here is the example:
var original = {
a: 1,
b: 2,
c: 3,
e: {
tester: 0,
combination: {
0: 1
}
},
0: {
test: "0",
2: "hello"
}
};
var badKeys = {
a: 1,
b: 2,
0: {
test: "0",
}
}
var expectedResult = {
c: 3,
e: {
tester: 0,
combination: {
0: 1
}
},
0: {
2: "hello"
}
}
I've tried using underscore difference function, but it doesn't work for objects, also not sure if this is the right function.
Can you help me to get the var expectedResult right?
You could use an iterative and recursive approach for geeting the wanted properties in a new object.
function deleteKeys(good, bad, result) {
Object.keys(good).forEach(function (key) {
if (bad[key] && typeof bad[key] === 'object') {
result[key] = {};
deleteKeys(good[key], bad[key], result[key]);
return;
}
if (!(key in bad) || good[key] !== bad[key]) {
result[key] = good[key];
}
});
}
var original = { a: 1, b: 2, c: 3, e: { tester: 0, combination: { 0: 1 } }, 0: { test: "0", 2: "hello", another: { a: { B: 2, C: { a: 3 } }, b: 2 } } },
badKeys = { a: 1, b: 2, 0: { test: "0", random: 2, another: { a: 1 } } },
result = {};
deleteKeys(original, badKeys, result);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This would be the algorithm:
function removeDifferences (original, removeKeys) {
// Get keys of to be deleted properties.
var keys = Object.keys(removeKeys);
// Iterate all properties on removeKeys.
for (key of keys) {
// Check if property exists on original.
if (typeof original[key] !== undefined) {
// If the property is an object, call same function to remove properties.
if (typeof removeKeys[key] === 'object') {
removeDifferences(original[key], removeKeys[key]);
} else {
delete original[key];
}
}
}
return original;
}
Applied to your case:
/* Your data. */
var original = {
a: 1,
b: 2,
c: 3,
e: {
tester: 0,
combination: {
0: 1
}
},
0: {
test: "0",
2: "hello"
}
};
var badKeys = {
a: 1,
b: 2,
0: {
test: "0",
}
};
var expectedResult = {
c: 3,
e: {
tester: 0,
combination: {
0: 1
}
},
0: {
2: "hello"
}
};
/* Function */
function removeDifferences(original, removeKeys) {
// Get keys of to be deleted properties.
var keys = Object.keys(removeKeys);
// Iterate all properties on removeKeys.
for (key of keys) {
// Check if property exists on original.
if (typeof original[key] !== undefined) {
// If the property is an object, call same function to remove properties.
if (typeof removeKeys[key] === 'object') {
removeDifferences(original[key], removeKeys[key]);
} else {
delete original[key];
}
}
}
return original;
}
/* Application */
var output = removeDifferences(original, badKeys);
console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can create recursive function that will return new object using for...in loop.
var original = {"0":{"2":"hello","test":"0"},"a":1,"b":2,"c":3,"e":{"tester":0,"combination":{"0":1}}}
var badKeys = {"0":{"test":"0"},"a":1,"b":2}
function remove(o1, o2) {
var result = {}
for (var i in o1) {
if (!o2[i]) result[i] = o1[i]
else if (o2[i]) {
if (typeof o1[i] == 'object' && typeof o2[i] == 'object') {
result[i] = Object.assign(result[i] || {}, remove(o1[i], o2[i]))
} else if (o1[i] != o2[i]) result[i] = o1[i]
}
}
return result
}
console.log(remove(original, badKeys))
Truly a job for some recursion and a bit of functional programming using a pure function. (Tested with Node v7.7.1)
"DoForAllNestedObjects" for applying some function "whattodo" on "every leaf on the dictionary tree" when there is an corresponding "leaf" in baddict.
let DoForAllNestedValues = (dict, baddict, whattodo) => {
for (let key in dict) {
if (typeof (dict[key]) === 'object' && typeof (baddict[key]) === 'object')
DoForAllNestedValues(dict[key], baddict[key], whattodo);
else
if (baddict[key])
whattodo(dict, key);
}
}
DoForAllNestedValues(original, badKeys, (obj, val) => delete obj[val]);
console.log(original);

Is there anything like 'lies in' operator in js, as we have '$in' in mongoose/mongo

i want to implement a function like this in js
function(arrayOfObjects, arrayOfValues, property) {
/*here i want to return all the objects of 'arrayOfObjects'
which satisfies the following condition
(index is the iterative index of array 'arrayOfObjects')
arrayOfObjects[index][property] is equivalent to any of
the values that lies in arrayOfValues */
};
example :
arrayOfObjects = [{ a: 1, b: 2 }, { a: 3, b:4 }, { a: 1, b :3 }];
arrayOfValues = [ 2, 3 ];
function(arrayOfObjects, arrayOfValues, 'b')
should return [{ a: 1, b: 2 }, { a: 1, b :3 }]
arrayOfObjects.filter(function (elem) {
return elem.hasOwnProperty(property)
&& -1 !== arrayOfValues.indexOf(elem[property]);
});
In case you need IE8 support: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility
You can use the Array.prototype.filter functionality:
var a1 = [{ a: 1, b: 2 }, { a: 3, b:4 }, { a: 1, b :3 }];
var a2 = [2, 3];
var filtered = a1.filter(function(item) {
return a2.indexOf(item.b) != -1;
});
No, this is too complex problem to have some build-in operator or function in JS.
You must use some cycle to walk through the elements.
function(arrayOfObjects, arrayOfValues, property) {
var result = [];
for (var i = 0; i < arrayOfObjects; i++) {
for (var j = 0; j < arrayOfValues; j++) {
if (arrayOfObjects[i][property] === arrayOfValues[j]) {
result.push(arrayOfObjects[i]);
continue; //object already added, go to next one
}
}
}
return result;
};
function fun1(arrayOfObjects, arrayOfValues, property) {
var result = new Array();
for (var obj in arrayOfObjects){
if (arrayOfObjects[obj].hasOwnProperty(property) &&
arrayOfObjects[obj][property] == arrayOfValues[property]){
result.push(arrayOfObjects[obj]);
}
}
return result;
}
var arrayOfObjects = [{ a: 1, b: 2 }, { a: 3, b:4 }, { a: 1, b :3 }];
var arrayOfValues = new Array();
arrayOfValues['a'] = 2;
arrayOfValues['b'] = 3;
console.log(fun1(arrayOfObjects , arrayOfValues , 'b'));
Your arrayOfValues should be an 'associative array' or key-value-pair object which use key to match the property. This might more suit your logic.

Merge JS objects without overwriting

Suppose you have two objects:
var foo = {
a : 1,
b : 2
};
var bar = {
a : 3,
b : 4
}
What's the best way to merge them (and allow deep merging) to create this:
var foobar = {
a : [1, 3],
b : [2, 4]
}
Edit for question clarification: Ideally, in the case of an existing property in one and not the other, I would expect an array to still be created, for normalization purposes and to allow for further reduction of the map, however the answers I'm seeing below are more than sufficient. For the purposes of this exercise, I was only looking for string or numerical merges, so I hadn't entertained every possible situational case. If you held a gun to my head and asked me to make a choice, though, I'd say default to arrays.
Thanks all for your contributions.
This ought to do what you're looking for. It will recursively merge arbitrarily deep objects into arrays.
// deepmerge by Zachary Murray (dremelofdeath) CC-BY-SA 3.0
function deepmerge(foo, bar) {
var merged = {};
for (var each in bar) {
if (foo.hasOwnProperty(each) && bar.hasOwnProperty(each)) {
if (typeof(foo[each]) == "object" && typeof(bar[each]) == "object") {
merged[each] = deepmerge(foo[each], bar[each]);
} else {
merged[each] = [foo[each], bar[each]];
}
} else if(bar.hasOwnProperty(each)) {
merged[each] = bar[each];
}
}
for (var each in foo) {
if (!(each in bar) && foo.hasOwnProperty(each)) {
merged[each] = foo[each];
}
}
return merged;
}
And this one will do the same, except that the merged object will include copies of inherited properties. This probably isn't what you're looking for (as per RobG's comments below), but if that is actually what you are looking for, then here it is:
// deepmerge_inh by Zachary Murray (dremelofdeath) CC-BY-SA 3.0
function deepmerge_inh(foo, bar) {
var merged = {};
for (var each in bar) {
if (each in foo) {
if (typeof(foo[each]) == "object" && typeof(bar[each]) == "object") {
merged[each] = deepmerge(foo[each], bar[each]);
} else {
merged[each] = [foo[each], bar[each]];
}
} else {
merged[each] = bar[each];
}
}
for (var each in foo) {
if (!(each in bar)) {
merged[each] = foo[each];
}
}
return merged;
}
I tried it out with your example on http://jsconsole.com, and it worked fine:
deepmerge(foo, bar)
{"a": [1, 3], "b": [2, 4]}
bar
{"a": 3, "b": 4}
foo
{"a": 1, "b": 2}
Slightly more complicated objects worked as well:
deepmerge(as, po)
{"a": ["asdf", "poui"], "b": 4, "c": {"q": [1, 444], "w": [function () {return 5;}, function () {return 1123;}]}, "o": {"b": {"t": "cats"}, "q": 7}, "p": 764}
po
{"a": "poui", "c": {"q": 444, "w": function () {return 1123;}}, "o": {"b": {"t": "cats"}, "q": 7}, "p": 764}
as
{"a": "asdf", "b": 4, "c": {"q": 1, "w": function () {return 5;}}}
Presumably you would iterate over one object and copy its property names to a new object and values to arrays assigned to those properties. Iterate over subsequent objects, adding properties and arrays if they don't already exist or adding their values to existing properties and arrays.
e.g.
function mergeObjects(a, b, c) {
c = c || {};
var p;
for (p in a) {
if (a.hasOwnProperty(p)) {
if (c.hasOwnProperty(p)) {
c[p].push(a[p]);
} else {
c[p] = [a[p]];
}
}
}
for (p in b) {
if (b.hasOwnProperty(p)) {
if (c.hasOwnProperty(p)) {
c[p].push(b[p]);
} else {
c[p] = [b[p]];
}
}
}
return c;
}
You could modify it to handle any number of objects by iterating over the arguments supplied, but that would make passing the object to merge into more difficult.
https://lodash.com/docs/3.10.1#merge
// using a customizer callback
var object = {
'fruits': ['apple'],
'vegetables': ['beet']
};
var other = {
'fruits': ['banana'],
'vegetables': ['carrot']
};
_.merge(object, other, function(a, b) {
if (_.isArray(a)) {
return a.concat(b);
}
});
// → { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
I just leave it here.
Shallow merging with values as arrays.
const foo = {a: 1, b: 2}
const bar = {a: 2, с: 4}
const baz = {a: 3, b: 3}
const myMerge = (...args) => args
.flatMap(Object.entries)
.reduce((acc, [key, value]) => {
acc[key] ??= []
acc[key].push(value)
return acc
}, {})
console.log(myMerge(foo, bar, baz))
//{ a: [1, 2, 3],
// b: [2, 3],
// с: [4] }
.as-console-wrapper { max-height: 100% !important; top: 0 }

Categories