I am looping though an object to retrieve some properties but for some reason I cannot access the value of a nested object property.
This is my looping function:
parseContacts = (contacts) => {
return contacts.map(contact => {
let parsedContact = {};
Object.keys(contact).forEach(key => {
if (key === 'givenName') {
parsedContact.firstName = contact[key];
} if (key === 'familyName') {
parsedContact.surname = contact[key];
} if (key === 'phoneNumbers') {
parsedContact.phoneNumber = contact[key][0].number;
}
})
return parsedContact;
})
}
firstName and surname work fine, but in the last if statement I get undefined. the property with key phoneNumbers it is an array of objects, and this is item 0 in the array:
{id: "302", label: "mobile", number: "+44 7X7X 50XX72"}
When I use this code instead:
} if (key === 'phoneNumbers') {
parsedContact.phoneNumber = contact[key][0];
}
without .number on the end I get the whole object back fine, I just can't get back only the number property from the object.
Update
On closer inspection, the array which has over 800 large objects in, some of the lengths of the phoneNumbers arrays were 0 so the property did not exist. This was causing the whole function to fail. Thanks for the comments below.
My solution was to add to the if statement:
if (key === 'phoneNumbers' && contact[key].length)
You can prevent the empty phoneNumbers array issue like this:
contact[key] && contact[key].length ? contact[key][0].number : ''
const parseContacts = contacts => {
return contacts.map(contact => {
let parsedContact = {}
Object.keys(contact).forEach(key => {
switch (key) {
case 'givenName':
parsedContact.firstName = contact[key]
break
case 'familyName':
parsedContact.surname = contact[key]
break
case 'phoneNumbers':
parsedContact.phoneNumber = contact[key] && contact[key].length ? contact[key][0].number : ''
}
})
return parsedContact
})
}
const contacts = []
for (let i = 0; i < 10; i++) {
contacts.push({
givenName: faker.name.firstName(),
familyName: faker.name.lastName(),
phoneNumbers: [
{
id: faker.random.uuid(),
label: 'mobile',
number: faker.phone.phoneNumber()
}, {
id: faker.random.uuid(),
label: 'mobile',
number: faker.phone.phoneNumber()
}
]
})
}
contacts.push({
givenName: faker.name.firstName(),
familyName: faker.name.lastName(),
phoneNumbers: []
})
contacts.push({
givenName: faker.name.firstName(),
familyName: faker.name.lastName(),
phoneNumbers: null
})
console.log('RESULT ' + JSON.stringify(parseContacts(contacts)))
<script src="https://rawgit.com/Marak/faker.js/master/examples/browser/js/faker.js"></script>
Related
I want to check a bunch of variables for falsy values.
Instead of writing a very long if statement, e.g.:
if (!userData.email || !userData.lastName || ! userData.firstName etc...
I wanted to put these values in an array and simply check the array, e.g.:
var detailsConditionsArr = [
userData.firstName,
userData.lastName,
userData.email,
userData.phone,
userData.address,
userData.mailAddress,
]
if (detailsConditionsArr.indexOf(false) === 1) {
console.log("Found a falsy");
}
But the above doesn't work (if statement never fires).
The values could be false, null or ''.
Is there a way to check these from the array?
Ideal use case for Array#some
Check for falsy value in the iteration, if the test succeeds, true will be returned.
var A = ["Name", ""];
var B = ["Name", "30"];
var C = ["Name", "30", null];
if (A.some(el => !el)) {
console.log("Found a falsy in A");
}
if (B.some(el => !el)) {
console.log("Found a falsy in B");
}
if (C.some(el => !el)) {
console.log("Found a falsy in C");
}
You can use the key names you want to check as an array, then do the check like this:
const userData = { email: '', lastName: '', firstName: '' };
const hasFalsy = ['email', 'lastName', 'firstName'].some(k => !Boolean(userData[k]));
if (hasFalsy) {
console.log('found a falsy');
}
You can use reduce to return a single value from the array like so:
var detailsConditionsArr = [
userData.firstName,
userData.lastName,
userData.email,
userData.phone,
userData.address,
userData.mailAddress,
]
if (detailsConditionsArr.reduce((carry, el)=> el ? carry : true, false)) {
console.log("Found a falsy");
}
You can loop the list of properties and then check if any is falsy :
const userData = {
firstName : 'John',
lastName : 'Doe',
email : null,
phone : 42,
address : 'Hello',
mailAddress : 'world'
};
function HasObjectAnyFalsyValues(obj)
{
for (let prop in obj)
{
if (Object.prototype.hasOwnProperty.call(obj, prop)
&& !obj[prop]) { // <-- check for falsy
return true;
}
}
return false;
}
console.log('The object has ' + (!HasObjectAnyFalsyValues(userData) ? 'no ' : '') + 'falsy values');
This will check all properties. If you want to list specific properties instead, use one of the other answers posted
You can use some method.
var detailsConditionsArr = [
"test",
12,
false,
"s",
"sd",
"test",
]
if (detailsConditionsArr.some(x => !x)) {
console.log("Found a falsy");
}
// or
//if (detailsConditionsArr.some(x => !Boolean(x))) {
// console.log("Found a falsy");
//}
Using some is a clean way for doing this job.
The Array.prototype.some() method tests whether at least one element in the array passes the test implemented by the provided function (in your case, falsy value).
const userData = {};
userData.firstName = "dd";
userData.lastName = false;
userData.email = 0;
userData.phone = 123;
userData.address = "aad";
userData.mailAddress = "dff";
const detailsConditionsArr = [
userData.firstName,
userData.lastName,
userData.email,
userData.phone,
userData.address,
userData.mailAddress,
]
const detailsConditionsArr2 = [true, 1, "fff"];
const doesHaveFalsy = obj => obj.some(x => !x);
if (doesHaveFalsy(detailsConditionsArr)) {
console.log("detailsConditionsArr Found a falsy");
}
if (doesHaveFalsy(detailsConditionsArr2)) {
console.log("detailsConditionsArr2 Found a falsy");
}
More about some - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
How can I extract the 'jobs' object from a nested json list like this:
result:
{
person:
[
{
name: ""
address: ""
jobs: [
{
company:""
},
{
company:""
}
]
}
]
}
Thank you
Write a generic method to extract object properties.
function onExtract(key, data) {
if (isObject(data)) {
for (let item in data) {
if (key === item) {
return data[item];
}
const res = onExtract(key, data[item]);
if (res !== null) return res;
}
}
if (isArray(data)) {
for (let item of data) {
const res = onExtract(key, item);
if (res !== null) return res;
}
}
return null;
}
function isObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
}
function isArray(arr) {
return Object.prototype.toString.call(arr) === "[object Array]";
}
// test
const data = {
person: [
{
name: "",
address: "",
jobs: [
{
company: ""
},
{
company: ""
}
]
}
]
};
console.log(onExtract("jobs", data));
let's say you have a return var that contains this json value
let mappedCompanies = return.person.map(person =>
person.jobs.map(job => job.company)
).flatMap(m => m)
mappedCompanies would contain an array with all the companies names for each one of the registers in "person", all as one array of strings
you can read more about Array.map() here: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Array/map
A dynamic way to query the person[] and find jobs, is to use the javascript map() method.
Here is the code without comments.
const personsJobs = (personName, personAddress) => {
const jobs = result.person.map((el) => {
if (el.name === personName && el.address === personAddress) {
return el.jobs;
} else {
return null;
}
})
.filter((el) => el !== null);
return jobs;
};
console.log(personsJobs("wyatt", "1234 test ln"));
Here is the code with comments to explain how the personsJob function works.
// Blow is an ES6 arrow function with the parameters 'personName' and 'personAddress',
// which represents the person in which you are querying for jobs (using both a persons
// name and address so in the case of persons with the same name, you only find the jobs
// of the person you want).
const personsJobs = (personName, personAddress) => {
// Since 'person' is an array, we can use the 'map' method as stated before, which
// will create a new array (jobs) that will store the jobs a specific person has.
const jobs = result.person.map((el) => {
// el stands for the current position in the person array.
// if el's (the current person) name and address values are equal to that of the
// parameters personName and personAddress, then that persons jobs are added to the jobs // array, however, if el does not satisfy the two parameters, null is added to the jobs
// array.
// The array, upon completion, will look something like this: ["programmer", null, null]
if (el.name === personName && el.address === personAddress) {
return el.jobs;
} else {
return null;
}
})
// Finally, the filter method is called to remove all null values so that you will
// only have the persons job in the jobs array.
// After filtering, the array will look like this: ["programmer"]
.filter((el) => el !== null);
return jobs;
};
// Prints the array of wyatt's jobs
console.log(personsJobs("wyatt", "1234 test ln"));
So, following the conclusion of the function, you will have dynamically found the jobs of a specific person.
you can use flatMap function like:
const jobsData = result.person.flatMap(item => item.jobs);
Here is a flexible solution using object-scan
// const objectScan = require('object-scan');
const data = { person: [{ name: '', address: '', jobs: [{ company: '' }, { company: '' }] }] };
console.log(objectScan(['person[*].jobs'], { reverse: false, rtn: 'value' })(data));
// => [ [ { company: '' }, { company: '' } ] ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#14.0.0"></script>
Disclaimer: I'm the author of object-scan
This question already has answers here:
Javascript reflection: Get nested objects path
(3 answers)
Get all paths to a specific key in a deeply nested object
(1 answer)
Get nested objects key as joined string
(2 answers)
Closed 2 years ago.
Code:
const obj = {
client: {
id: 1,
personal: {
name: "Mike"
}
},
address: {
street: "streetname"
}
};
function recursiveKeys(obj) {
Object.keys(obj).forEach((key) => {
if (typeof obj === "object") {
Object.keys(obj[key]).forEach((innerKey) => {
console.log(`${key}.${innerKey}`);
});
}
});
}
recursiveKeys(obj);
Desired output:
client.id
client.personal.name
address.street
This code works only for a 2 level object, but it won't work for a 3rd-4th level and deeper, is there a clean way to achieve this?
You need to make your recursiveKeys actually recursive. Pass along the partial property string from the parent object on each recursive call.
const obj = {
client: {
id: 1,
personal: {
name: "Mike"
}
},
address: {
street: "streetname"
}
};
function recursiveKeys(obj, propStr = '') {
Object.entries(obj).forEach(([key, val]) => {
const nestedPropStr = propStr + (propStr ? '.' : '') + key;
if (typeof val === 'object') recursiveKeys(val, nestedPropStr);
else console.log(nestedPropStr);
});
}
recursiveKeys(obj);
This code works only for a 2 level object, but it won't work for a
3rd-4th level and deeper, is there a clean way to achieve this?
The problem is you should make your recursiveKeys as it is with 3 steps:
Determine the key result named keyRes
Check if the inner content is an object, then recursive it.
Print the keyRes along with getting out of the recursive, important to avoid infinite loop !!!
const obj = {
client: {
id: 1,
personal: {
name: "Mike"
}
},
address: {
street: "streetname"
}
};
function recursiveKeys(obj, previousKey = '') {
Object.entries(obj).forEach(([key, values]) => {
let keyRes = previousKey ? `${previousKey}.${key}` : key; // Step 1
if (typeof values === 'object') // Step 2
recursiveKeys(values, keyRes);
else // Step 3
console.log(keyRes);
});
}
recursiveKeys(obj);
The answer is: recursion!
const obj = {
client: {
id: 1,
personal: {
name: "Mike"
}
},
address: {
street: "streetname"
}
};
function recursiveKeys(obj) {
const keys = []
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === "object" && obj[key]) {
//vvvvvvvvvvvvvvvvvvvvvvv--- the function calls itself
recursiveKeys(obj[key]).forEach(innerKey => {
keys.push(`${key}.${innerKey}`)
})
}else{
keys.push(key)
}
});
return keys
}
console.log(recursiveKeys(obj));
If you have access to the new Array#flatMap() method, you can use it to make this even more elegant:
const obj = {
client: {
id: 1,
personal: {
name: "Mike"
}
},
address: {
street: "streetname"
}
};
function recursiveKeys(obj) {
return Object.keys(obj).flatMap(key =>
typeof obj[key] === "object" && obj[key]
? recursiveKeys(obj[key]).map(innerKey => `${key}.${innerKey}`)
: key
);
}
console.log(recursiveKeys(obj));
I have a list of objects:
[{name: 'Elza'}, {name: 'Tom'}, {name: 'Elza'}]
I use the below methods to get duplicated objects(by name) and assign a prop isDuplicated:
const duplicatedNames = arrayOfObjects
.map(e => e['name'])
.map((e, i, final) => final.indexOf(e) !== i && i++)
.filter(obj => arrayOfObjects[obj])
.map(e => !arrayOfObjects[e]['name']);
const result = arrayOfObjects.filter((obj, i) => {
return duplicatedNames.includes(obj.name) && Object.assign(obj, { isDuplicated: true });
});
I receive an array like:
[{name: 'Elza', isDuplicated: true}, {name: 'Tom'}, {name: 'Elza', isDuplicated: true}]
I would like to mark only the second occurrence of duplicate- so i would like the result to be:
[{name: 'Elza'}, {name: 'Tom'}, {name: 'Elza', isDuplicated: true}]
Can anyone know how to do it base on my code?
Here is a function that checks if a name exist more then once.
let data = [{name:'Elza'}, {name:'Tom'}, {name:'Elza'}, {name: "Jerry"}, {name: "Jerry"}];
function checkDup(arr){
let cache = [];
return arr.map(({name}, index) => {
if(!cache.find(el => el.name == name)){
cache.push({name, index});
return {name, index};
}
let { index: cacheIndex } = cache.find(el => el.name === name);
return {name,index: cacheIndex , isDuplicated: true};
})
}
console.log(checkDup(data));
You could create a Set of names. If the size of the set is same as after the name has been added, then it's duplicate record.
const input = [{name:'Elza'}, {name:'Tom'}, {name:'Elza'}],
names = new Set;
for (const o of input)
if (names.size === names.add(o.name).size)
o.isDuplicate = true
console.log(input)
You can try this:
let users = [{name:'Elza'}, {name:'Tom'}, {name:'Elza'}]
let flags = [], output = [];
users.forEach(user => {
if (flags[user.name]) {
output.forEach(item => {
if (item.name === user.name) {
item.isDuplicated = true
output.push(user);
}
})
} else {
flags[user.name] = true;
output.push(user);
}
})
Given your original array A, you could create a temporary array B and, for each a element of A, check:
if B contains a.name, then set a.isDuplicated to true;
else, push a.name in B.
let A = [{name: 'Elza'}, {name: 'Tom'}, {name: 'Elza'}];
let B = [];
A.forEach(a => {
if (B.includes(a.name)) {
a.isDuplicated = true;
} else {
B.push(a.name);
}
});
console.log(A);
You can use reduce with a helper object:
const collection = [{ name: 'Elza'}, { name: 'Tom'}, { name: 'Elza' }]
const helper = {}
const result = collection.reduce((acc, { name }) => {
if (helper[name]) {
return [...acc, { name, isDuplicate: true }]
}
helper[name] = 'x';
return [...acc, { name }]
}, [])
console.log(result)
I need to filter some data based on multiple values. Language, title and slug
[
{
de: "4567uy55",
en: "654321",
lang: [
{
id: "654321",
language: "English",
title: "Title1"
},
{
id: "4567uy55",
language: "German",
title: "Title2"
}
],
slug: 'some-slug'
},
...
]
What I have now returns all objects which have one or part of the filters(in case title is This is a title, the word this should match), but I need to return objects which have all of them.
I used an object flattner just to get all properties and values in one object, but I can't get it to filter the way I need it.
multiFilter = (arr, filters) => {
console.log(filters)
console.log(arr)
let newArray = []
for (let c of arr) {
let flatCourse = flatten(c)
for (let k in flatCourse) {
const keyArr = k.split('/')
const filterKeys = Object.keys(filters)
Object.keys(filters).map((key) => {
if (keyArr.includes(key)) {
const flatVal = flatCourse[k].toString().toLowerCase()
const filterVal = filters[key].toString().toLowerCase()
console.log(flatVal)
console.log(filterVal)
if (flatVal.includes(filterVal)) {
arr = []
arr.push(c)
newArray.push(c)
}
}
})
}
}
return newArray
}
Filters look like this:
[
language:["English"],
title: ["Some title"],
slug:["some slug"]
]
Instead of mixing for loops and functional chaining you could just go with one of them:
multiFilter = (arr, filters) =>
arr.map(flatten).filter(el => // filter out elements from arr
Object.entries(filters).every(([fKey, fValues]) => // ensure that every key is included in the object
Object.entries(el).some(([oKey, oValue]) =>
oKey.split("/").includes(fKey) && fValues.includes(oValue)// make sure that at least one of the values equals the elements value
)
)
);
arr.filter(course => {
// Returns an array of booleans corresponding to whether or not each filter condition is satisfied
return Object.keys(filters).map(key => {
return filters[key].map(filter => {
// Special case here because lang is an array
if (key == 'language' && course.lang != undefined) {
return course.lang.some(lang => lang[key].includes(filter))
}
if (course[key] == undefined) {
return false
}
return course[key].includes(filter)
}).every(result => result == true)
}).every(result => result == true)
})