Check if array includes a value - javascript

I have an array of objects, and I need to prevent duplicate objects of being added to the array. I've tried the following code to check for duplicates:
const array = [{ name: 'John' }];
const newName = {
name: 'John'
};
console.log(array);
console.log(newName);
if (array.includes(newName)) {
console.log(newName.name + ' exists');
} else {
console.log('name does not exist');
}
Console logging tells that the object NewName is exactly the same as array[0]. However,
if(array.includes(newName))
returns false.
What I'm I doing wrong? I tried the solutions in this post as well with no luck:
How do I check if an array includes an object in JavaScript?

Simply, you can use array.some from
Array.prototype.some() documentation.
In your own example, you can do some tweaks to your program:
const array = [{ name: "John" }];
const newName = {
name: "John"
};
console.log(array);
console.log(newName);
if (array.some(object => object.name === newName.name)) {
console.log(newName.name + " exists");
} else {
console.log("name does not exist");
}

If the name is the identity of the object, you can use some function on array:
const array = [{ name: 'John' }];
const newName = { name: 'John' };
if (array.some(({name}) => name === newName.name)) {
console.log(newName.name + ' exists');
} else {
console.log('name does not exist');
}
Or you can check if the count of properties is the same and then every property with:
const array = [{ name: 'John', age: 33 }, { name: 'John', age: 45 }];
const newName = { age: 33, name: 'John' };
if (array.some(x => Object.keys(x).length === Object.keys(newName).length && Object.keys(x).every(p => x[p] === newName[p]))) {
console.log(newName.name + ' exists');
} else {
console.log('name does not exist');
}

The thing you're missing is that includes checks for identity when you use it on an object. newName has the same properties as the object in your array, but it isn't the same object any more than two people named John are the same person. For a more obvious example, run {} == {} and you'll get false.
To check if the array contains an object with the same name, you can use some and pass it a function to compare the object, e.g.
array.some(e => e.name == newName.name)

use it :
var newName = {
name: 'John'
};
console.log(array);
console.log(newName.name);
var found = array.some(obj => obj.name === newName.name);
if (found) {
console.log(newName.name + ' exists');
} else {
console.log('name does not exist');
}

const array = [{
name: 'John'
}];
const newName = {
name: 'John'
};
let resultArr = array.filter((item) => {
return item.name === newName.name
})
let elementPresentMsg;
if (resultArr.length > 0) {
elementPresentMsg = newName.name + ' exists';
} else {
elementPresentMsg = 'name does not exist'
}
document.getElementById('msg').innerHTML = elementPresentMsg;
<html>
<body>
<p id="msg"></p>
</body>
</html>
If you want to find the name or any other value from array, if attributes of object are same as that in array of objects, the following should be helpful:
const array = [{ name: 'John' }];
const newName = {
name: 'John'
};
let resultArr = array.filter((item) => {
return item.name === newName.name
})
if (resultArr.length > 0) {
alert(newName.name + ' exists'); //any way to print result instead of alert
} else {
alert('name does not exist'); //any way to print result instead of alert
}

That is because array[0] not equal newName. In Javascript, they point to the different element. You can try console.log(array[0] == newName), and you will get false.
If you want to find the duplicates, you can try like this:
if (JSON.stringify(array).indexOf(JSON.stringify(newName)) != -1) {
console.log('exist')
}
else {
console.log('not exist')
}

This is because you are trying to compare two different instances of objects which will never be equal.
But if you convert the objects to primitive strings by doing JSON.stringify you can then use Array.includes for deep comparison. As ttulka pointed out, this will only work if the object to be searched has the properties in the same order as the object to find in the array.
const array = [{ name: 'John', age: 33 }];
const newName = {
name: 'John',
age: 33
};
if (array.map(obj => JSON.stringify(obj)).includes(JSON.stringify(newName))) {
console.log(newName.name + ' exists');
} else {
console.log('name does not exist');
}
If you look into the Array#includes polyfill in MDN, you would see that the comparison happens using the === strict equality operator:
function sameValueZero(x, y) {
return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
}
// 7. Repeat, while k < len
while (k < len) {
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(valueToFind, elementK) is true, return true.
if (sameValueZero(o[k], valueToFind)) {
return true;
}
// c. Increase k by 1.
k++;
}
So when you compare two different object literals using === they won't be equal:
console.log({name :"John"} === {name :"John"});
But primitive strings on the other hand are equal if you compare them with ===:
console.log('{name:"John"}' === '{name:"John"}');
But the same is not true for String objects:
console.log(new String('{name:"John"}') === new String('{name:"John"}'));

Related

Set adds same object to the list - Javascript

Is there a way to check if the objects have the same that before inserting them into the Set?
let mySet = new Set();
let person = {
name: 'John',
age: 21
};
let person2 = {
name: 'John',
age: 21
};
mySet.add(person);
mySet.add(person2);
console.log(JSON.stringify([...mySet]));
Is there a way to check if the objects have the same that before inserting them into the Set?
Only by doing it yourself by iterating the set, since they're different (though equivalent) objects; as is always the case, two different objects aren't "equal" to each other for any of JavaScript's built-in operations. And sets don't offer methods like some or find like arrays do.
For instance, you might use a utility function:
function setFind(set, predicate) {
for (const element of set) {
if (predicate(element)) {
return element;
}
}
}
Then:
if (!setFind(mySet, ({ name, age }) => name === person2.name && age == person2.age)) {
mySet.add(person2);
}
let mySet = new Set();
let person = {
name: 'John',
age: 21
};
let person2 = {
name: 'John',
age: 21
};
mySet.add(person);
if (!setFind(mySet, ({ name, age }) => name === person2.name && age == person2.age)) {
mySet.add(person2);
}
console.log(JSON.stringify([...mySet]));
function setFind(set, predicate) {
for (const element of set) {
if (predicate(element)) {
return element;
}
}
}
Or just use a loop, or use some or find after converting to an array:
let contains = [...mySet].some(({ name, age }) => name === person2.name && age == person2.age);
if (!contains) {
mySet.add(person2);
}
let mySet = new Set();
let person = {
name: 'John',
age: 21
};
let person2 = {
name: 'John',
age: 21
};
mySet.add(person);
let contains = [...mySet].some(({ name, age }) => name === person2.name && age == person2.age);
if (!contains) {
mySet.add(person2);
}
console.log(JSON.stringify([...mySet]));
Or similar.

JavaScript - iterate through object and change nested properties

This is an object to be processed:
var q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
I would like to go through each key of q and if the corresponding value is an object that has the property contains then replace it with $regex.
Related information can be found here: JavaScript: Object Rename Key
You can try the following way:
var q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
for(var k in q){
if(q[k].hasOwnProperty('contains')){
Object.defineProperty(q[k], '$regex',
Object.getOwnPropertyDescriptor(q[k], 'contains'));
delete q[k]['contains'];
}
}
console.log(q);
for(const obj of Object.values(q)) {
obj.$regex = obj.contains;
delete obj.contains;
}
Just go over all values inside q and copy the contains property into the $regex property.
To iterate over object keys first you have to fetch them, here is one simple approach
const keys = Object.keys(q); // ["email", "name"]
Now iterate over the array which we got and perform regex testing;
keys.forEach(key => {
let value = q[key].contains;
// create $regex and assign value
// remove .contains
})
You can loop through the objects and first put current value of contains property in $regex and then delete the contains property.
Below is working code:
var q = {
email: {
contains: "noname#hotmail.com"
},
name: {
contains: "someuser"
}
};
for (var i of Object.values(q)) {
if (i.hasOwnProperty("contains")) {
i.$regex = i.contains;
delete i.contains;
}
}
console.log(q);
var q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
Object.keys(q).forEach(k => {
if (typeof q[k].contains != 'undefined'){
q[k].$regex = q[k].contains;
delete q[k].contains;
}
})
console.log(q);
Other version using Es 6 features
const renameProp = (
oldProp,
newProp,
{ [oldProp]: old, ...others }
) => {
return {
[newProp]: old,
...others
};
};
let q = {
email: {contains: "noname#hotmail.com"},
name: {contains: "someuser"}
};
let newObj = {}
for (let propName in q) {
newObj[propName] = renameProp("contains","$regex",q[propName])
}
console.log(newObj)
var q = {
email: {
contains: "noname#hotmail.com"
},
name: {
contains: "someuser"
},
asdf: "asdf"
};
Object.keys(q).forEach(function(item, index) {
if (typeof q[item] == "object" && q[item].contains) {
q[item].$regex = q[item].contains;
delete q[item].contains;
}
})

How to get all the names inside a nested object?

I'm currently learning JavaScript and my teacher asked me to do an exercise that would return an array with all the names of this object:
{
name: 'grandma',
daughter: {
name: 'mother',
daughter: {
name: 'daughter',
daughter: {
name: 'granddaughter'
}
}
}
}
my question is similar to this one but the solution does not work for me because my object does not contain any arrays. The code I have so far:
function toArray(obj) {
const result = [];
for (const prop in obj) {
const value = obj[prop];
if (typeof value === 'object') {
result.push(toArray(value));
}
else {
result.push(value);
}
}
return result;
}
function nameMatrioska(target) {
return toArray(target);
}
which prints out this : [ 'grandma', [ 'mother', [ 'daughter', [Array] ] ] ]
but what my teacher wants is: ['grandma', 'mother', 'daughter', 'granddaughter']
codepen
Obviously you push an array to an array, where all nested children appears as an array.
To solve this problem, you could iterate the array and push only single items to the result set.
A different method is, to use some built-in techniques, which works with an array, and returns a single array without a nested array.
Some methods:
Array#concat, creates a new array. It works with older Javascript versions as well.
result = result.concat(toArray(value));
Array#push with an array and Function#apply for taking an array as parameter list. It works in situ and with older versions of JS.
Array.prototype.push.apply(result, toArray(value));
[].push.apply(result, toArray(value)); // needs extra empty array
Spread syntax ... for spreading an array as parameters. ES6
result.push(...toArray(value));
Spread syntax is a powerful replacement for apply with a greater use. Please the the examples as well.
Finally an example with spread syntax.
function toArray(obj) {
const result = [];
for (const prop in obj) {
const value = obj[prop];
if (value && typeof value === 'object') { // exclude null
result.push(...toArray(value));
// ^^^ spread the array
}
else {
result.push(value);
}
}
return result;
}
function nameMatrioska(target) {
return toArray(target);
}
var object = { name: 'grandma', daughter: { name: 'mother', daughter: { name: 'daughter', daughter: { name: 'granddaughter' } } } };
console.log(nameMatrioska(object));
You need .concat instead of .push. Push adds one item to an array; concat joins two arrays together.
['grandmother'].concat(['mother', 'daughter'])
-> ['grandmother', 'mother', 'daughter']
Unlike push, which modifies the array you call it on, concat creates a new array.
var a1 = [ 'grandmother' ];
a1.push( 'mother' );
console.log( a1 );
-> ['grandmother', 'mother']
var a2 = [ 'steve' ];
var result = a2.concat(['Jesus', 'Pedro']);
console.log( a1 );
-> ['steve']
console.log( result );
-> ['steve', 'Jesus', 'Pedro']
Try this
function toArray(obj) {
var result = "";
for (const prop in obj) {
const value = obj[prop];
if (typeof value === 'object') {
result = result.concat(" " + toArray(value));
}
else {
result = result.concat(value);
}
}
return result;
}
function nameMatrioska(target) {
return toArray(target).split(" ");
}
function toArray(obj) {
var result = [];
for (var prop in obj) {
var value = obj[prop];
if (typeof value === 'object') {
result = result.concat(toArray(value))
} else {
result.push(value);
}
}
return result;
}
function nameMatrioska(target) {
return toArray(target);
}
//USER
var names = {
name: 'grandma',
daughter: {
name: 'mother',
daughter: {
name: 'daughter',
daughter: {
name: 'granddaughter'
}
}
}
};
console.log(nameMatrioska(names));
//Output: ["grandma", "mother", "daughter", "granddaughter"]
You are really close.
You have to flatten your array in your last step.
Tip: In general be careful when checking for type object because e.g. null, undefined are also objects in JavaScript world!
function isObject(value) {
if(value === undefined) return "Undefined";
if(value === null) return "Null";
const string = Object.prototype.toString.call(value);
return string.slice(8, -1);
}
function collectPropertiesRec(object, propertyName) {
const result = [ ];
for(const currentPropertyName in object) {
const value = object[currentPropertyName];
if(isObject(value) === 'Object') {
result.push(collectPropertiesRec(value, propertyName));
}
else if(currentPropertyName === propertyName) {
result.push(value);
}
}
return result;
}
function flattenDeep(arr1) {
return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), [ ]);
}
//USER
const names = {
name: 'grandma',
daughter: {
name: 'mother',
daughter: {
name: 'daughter',
daughter: {
name: 'granddaughter'
}
}
}
};
var result = collectPropertiesRec(names, "name");
alert(flattenDeep(result).join(", "));

How come this reduce method doesn't give me object back?

[['id', '1111'], ['name', 'aaaaa']]
I have a list like this.
{ id: '1111', name: 'aaaa' }
And, I want to format the list to something like this.
So, I've tried to convert the list to the object in that format with reduce method of JavaScript like the below.
But, it doesn't work!
Could anyone tell me what I'm doing wrong and how to fix it, please?
Code
const result = [['id', '1111'], ['name', 'aaaaa']].reduce(
(accumulator, list, index) => {
const KEY = list[0];
const VALUE = list[1];
console.log(accumulator)
if(KEY === 'id' || KEY === 'name') {
return accumulator[KEY] = VALUE;
}
return accumulator
},
{
id: '',
name: ''
}
);
log from the console.log(accumulator)
{ id: '', name: '' }
1111 // why this is not { id: '1111', name: '' } ???
Expected Result
{ id: '1111', name: 'aaaa' }
You should return accumulator in reduce callback, but the result of accumulator[KEY] = VALUE statement is not accmulator, so remove return key word in the if clause:
if(KEY === 'id' || KEY === 'name') {
accumulator[KEY] = VALUE;
}
Only return once. In the conditional only set the property
if(KEY === 'id' || KEY === 'name') {
/*return*/ accumulator[KEY] = VALUE;
// ^^ remove the return
}
You aren't returning the full object otherwise
This code is returning the value...remove the return statement.
if(KEY === 'id' || KEY === 'name') {
return accumulator[KEY] = VALUE;
}
To
if(KEY === 'id' || KEY === 'name') {
accumulator[KEY] = VALUE;
}

Convert javascript object camelCase keys to underscore_case

I want to be able to pass any javascript object containing camelCase keys through a method and return an object with underscore_case keys, mapped to the same values.
So, I have this:
var camelCased = {firstName: 'Jon', lastName: 'Smith'}
And I want a method to output this:
{first_name: 'Jon', last_name: 'Jon'}
What's the fastest way to write a method that takes any object with any number of key/value pairs and outputs the underscore_cased version of that object?
Here's your function to convert camelCase to underscored text (see the jsfiddle):
function camelToUnderscore(key) {
return key.replace( /([A-Z])/g, "_$1").toLowerCase();
}
console.log(camelToUnderscore('helloWorldWhatsUp'));
Then you can just loop (see the other jsfiddle):
var original = {
whatsUp: 'you',
myName: 'is Bob'
},
newObject = {};
function camelToUnderscore(key) {
return key.replace( /([A-Z])/g, "_$1" ).toLowerCase();
}
for(var camel in original) {
newObject[camelToUnderscore(camel)] = original[camel];
}
console.log(newObject);
If you have an object with children objects, you can use recursion and change all properties:
function camelCaseKeysToUnderscore(obj){
if (typeof(obj) != "object") return obj;
for(var oldName in obj){
// Camel to underscore
newName = oldName.replace(/([A-Z])/g, function($1){return "_"+$1.toLowerCase();});
// Only process if names are different
if (newName != oldName) {
// Check for the old property name to avoid a ReferenceError in strict mode.
if (obj.hasOwnProperty(oldName)) {
obj[newName] = obj[oldName];
delete obj[oldName];
}
}
// Recursion
if (typeof(obj[newName]) == "object") {
obj[newName] = camelCaseKeysToUnderscore(obj[newName]);
}
}
return obj;
}
So, with an object like this:
var obj = {
userId: 20,
userName: "John",
subItem: {
paramOne: "test",
paramTwo: false
}
}
newobj = camelCaseKeysToUnderscore(obj);
You'll get:
{
user_id: 20,
user_name: "John",
sub_item: {
param_one: "test",
param_two: false
}
}
es6 node solution below. to use, require this file, then pass object you want converted into the function and it will return the camelcased / snakecased copy of the object.
const snakecase = require('lodash.snakecase');
const traverseObj = (obj) => {
const traverseArr = (arr) => {
arr.forEach((v) => {
if (v) {
if (v.constructor === Object) {
traverseObj(v);
} else if (v.constructor === Array) {
traverseArr(v);
}
}
});
};
Object.keys(obj).forEach((k) => {
if (obj[k]) {
if (obj[k].constructor === Object) {
traverseObj(obj[k]);
} else if (obj[k].constructor === Array) {
traverseArr(obj[k]);
}
}
const sck = snakecase(k);
if (sck !== k) {
obj[sck] = obj[k];
delete obj[k];
}
});
};
module.exports = (o) => {
if (!o || o.constructor !== Object) return o;
const obj = Object.assign({}, o);
traverseObj(obj);
return obj;
};
Came across this exact problem when working between JS & python/ruby objects. I noticed the accepted solution is using for in which will throw eslint error messages at you ref: https://github.com/airbnb/javascript/issues/851 which alludes to rule 11.1 re: use of pure functions rather than side effects ref:https://github.com/airbnb/javascript#iterators--nope
To that end, figured i'd share the below which passed the said rules.
import { snakeCase } from 'lodash'; // or use the regex in the accepted answer
camelCase = obj => {
const camelCaseObj = {};
for (const key of Object.keys(obj)){
if (Object.prototype.hasOwnProperty.call(obj, key)) {
camelCaseObj[snakeCase(key)] = obj[key];
}
}
return camelCaseObj;
};
Marcos Dimitrio posted above with his conversion function, which works but is not a pure function as it changes the original object passed in, which may be an undesireable side effect. Below returns a new object that doesn't modify the original.
export function camelCaseKeysToSnake(obj){
if (typeof(obj) != "object") return obj;
let newObj = {...obj}
for(var oldName in newObj){
// Camel to underscore
let newName = oldName.replace(/([A-Z])/g, function($1){return "_"+$1.toLowerCase();});
// Only process if names are different
if (newName != oldName) {
// Check for the old property name to avoid a ReferenceError in strict mode.
if (newObj.hasOwnProperty(oldName)) {
newObj[newName] = newObj[oldName];
delete newObj[oldName];
}
}
// Recursion
if (typeof(newObj[newName]) == "object") {
newObj[newName] = camelCaseKeysToSnake(newObj[newName]);
}
}
return newObj;
}
this library does exactly that: case-converter
It converts snake_case to camelCase and vice versa
const caseConverter = require('case-converter')
const snakeCase = {
an_object: {
nested_string: 'nested content',
nested_array: [{ an_object: 'something' }]
},
an_array: [
{ zero_index: 0 },
{ one_index: 1 }
]
}
const camelCase = caseConverter.toCamelCase(snakeCase);
console.log(camelCase)
/*
{
anObject: {
nestedString: 'nested content',
nestedArray: [{ anObject: 'something' }]
},
anArray: [
{ zeroIndex: 0 },
{ oneIndex: 1 }
]
}
*/
following what's suggested above, case-converter library is deprectaed, use snakecase-keys instead -
https://github.com/bendrucker/snakecase-keys
supports also nested objects & exclusions.
Any of the above snakeCase functions can be used in a reduce function as well:
const snakeCase = [lodash / case-converter / homebrew]
const snakeCasedObject = Object.keys(obj).reduce((result, key) => ({
...result,
[snakeCase(key)]: obj[key],
}), {})
jsfiddle
//This function will rename one property to another in place
Object.prototype.renameProperty = function (oldName, newName) {
// Do nothing if the names are the same
if (oldName == newName) {
return this;
}
// Check for the old property name to avoid a ReferenceError in strict mode.
if (this.hasOwnProperty(oldName)) {
this[newName] = this[oldName];
delete this[oldName];
}
return this;
};
//rename this to something like camelCase to snakeCase
function doStuff(object) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
var r = property.replace(/([A-Z])/, function(v) { return '_' + v.toLowerCase(); });
console.log(object);
object.renameProperty(property, r);
console.log(object);
}
}
}
//example object
var camelCased = {firstName: 'Jon', lastName: 'Smith'};
doStuff(camelCased);
Note: remember to remove any and all console.logs as they aren't needed for production code

Categories