How to replace values of a dictionary in JS - javascript

Shouldn't this be creating a new dictionary and be able to replace its values everytime I call the getVal function?
var dict = [];
function getVal(inarr, element, data) {
var arr = inarr[element].map((item) => item[data]);
return arr;
};
console.log(getVal(event, 'foo', 'bar'));

function getVal(inarr, element, data) {
var arr = inarr[element].map((item) => item[data]);
return arr;
};
console.log(getVal(event, 'foo', 'bar'));

It's guess work at best because your question isn't clear and input is missing but you could try something like this:
//keys is an array of string or function
// if it's a function then it will pass current object
// to that function
function getMember(object,keys){
return keys.reduce(
function(acc,key){
if(acc===undefined){
return acc;
}
if(typeof key === "function"){
return key(acc);
}
return acc[key];
}
,object
)
}
function getVal(object) {
return getMember(
object,
[
"foo", // get member foo is an array of {bar:number}
function(x){ //map array of {bar:number} to array of number
return x.map(
function(x){
return x.bar;
});
}
]
);
};
var event = {
foo:[
{
bar:1
},
{
bar:2
}
]
};
console.log(getVal(event));// should log [1,2]

Related

Compare value within nested object

So I've been trying to find a solution to this for a little while with no luck.
const nameTest = 'testName';
const test = {
RANDOM_ONE: {
NAME: 'testName',
SOMETHING: {...}
},
RANDOM_TWO: {
NAME: 'Name',
SOMETHING: {...}
}
}
Is there any simple, easy way where I can compare the nameTest and the NAME key without knowing what the RANDOM_X is in order to access NAME?
You can use Object.keys() to get the array of all the keys. Then loop through the array to check the property:
const nameTest = 'testName';
const test = {
RANDOM_ONE: {
NAME: 'testName',
SOMETHING: {}
},
RANDOM_TWO: {
NAME: 'Name',
SOMETHING: {}
}
}
let testKeys = Object.keys(test);
testKeys.forEach(function(k){
console.log(test[k].NAME == nameTest);
});
You can use a for ... in loop:
for (let key in test) {
if (test[key].NAME === nameTest) {
// do something
}
}
I hope we know that 2 levels down into test is your object. You could write a function, to compare the name key.
function compare(obj, text){
for(let x in obj){
if(obj.x.name == text) return true;
else ;
}
}
Then call the function with your object and the string.
let a = compare(test, nameTest);
Note: this would compare the object to only ascertain if it contains the nameTest string.
var obj= test.filter(el){
if(el.NAME==nameTest)
{
return el;
}
}
var x= obj!=null?true:false;
You could use find.
The find method executes the callback function once for each index of
the array until it finds one where callback returns a true value. If
such an element is found, find immediately returns the value of that
element. Otherwise, find returns undefined.
So it is more memory efficient, than looping over the whole object with forEach, because find returns immediately if the callback function finds the value. Breaking the loop of forEach is impossible. In the documentation:
There is no way to stop or break a forEach() loop other than by
throwing an exception. If you need such behavior, the forEach() method
is the wrong tool.
1. If you want to get the whole object
var nameTest = 'testName';
var test = {
RANDOM_ONE: {
NAME: 'testName',
SOMETHING: {}
},
RANDOM_TWO: {
NAME: 'Name',
SOMETHING: {}
}
};
function getObjectByNameProperty(object, property) {
var objectKey = Object.keys(object)
.find(key => object[key].NAME === property);
return object[objectKey];
}
var object = getObjectByNameProperty(test, nameTest);
console.log(object);
2. If you just want to test if the object has the given name value
var nameTest = 'testName';
var test = {
RANDOM_ONE: {
NAME: 'testName',
SOMETHING: {}
},
RANDOM_TWO: {
NAME: 'Name',
SOMETHING: {}
}
};
function doesObjectHaveGivenName(object, nameValue) {
var objectKey = Object.keys(object)
.find(key => object[key].NAME === nameValue);
return objectKey ? true : false;
}
console.log( doesObjectHaveGivenName(test, nameTest) );

JavaScript: Creating a function to reject array elements found to be truthy, trouble with values in objects in the array

I'm working on a code challenge assignment. Create a function, reject, that takes an array and a callback function, and removes from the array any items that are found truthy when the callback function is run against them. I've written the following:
function reject(collection, callback) {
for (var i = 0; i < collection.length; i++) {
if(callback(collection[i]) === true){
collection.splice(i, 1);
}
}
return collection;
}
and where I'm hitting a wall is a test with an array of key-value pairs. The failing test:
var obj = {a:1, b:2, c:3, d:4};
var isOdd = function(value, key, collection) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
expect(evens).to.eql({b:2, d:4});
Lack of experience has exhausted my ability to search for answers effectively, so here we are. Any help or guidance is appreciated.
Edited to add:
Misread the tests in the original instructions (then failed to catch it when copy/pasting the test). I definitely know the difference between an object and an array, just thought I saw [{a:1, b:2, c:3, d:4}] in the document but it was actually ({a:1, b:2, c:3, d:4}) for whatever reason. Sorry.
I think this is what you're trying to do?
function reject(collection, callback) {
Object.keys(collection).forEach(function(key){
if(callback(collection[key], key, collection)){
delete collection[key];
}
});
return collection;
}
var obj = {a:1, b:2, c:3, d:4};
var isOdd = function(value, key, collection) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
console.log(evens); //prints { b: 2, d: 4 }
You have the right idea, however you need to look at the difference between a JavaScript object and array. Read this and learn the difference.
A JavaScript object does not have the property of .length to return the size of the collection. Use the following loop instead:
for (var key in collection)
A collection does not have the property .splice, that is for arrays. Instead of using .splice to remove the item use
delete collection[key]
Finally, pass the item in the collection to the callback
callback(collection[key])
Updated Answer:
function reject(collection, callback) {
for (var key in collection) {
if(callback(collection[key]) === true) {
delete collection[key];
}
}
return collection;
}
var obj = {a:1, b:2, c:3, d:4}; // Use for an object passed
var obj2 = [1, 2, 3, 4]; // Use as an array passed
var isOdd = function(value) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
console.log(evens);
// expect(evens).to.eql({b:2, d:4});
Here's the solution I ended up with. To clarify, the tests were passing in arrays and objects, so that's why I first had trouble (with the objects) and then there was some confusion in in the answers. I wrote:
function reject(collection, callback) {
if(Array.isArray(collection)){
for (var i = 0; i < collection.length; i++) {
if(callback(collection[i]) === true){
collection.splice(i, 1);
}
}
} else {
for (number in collection){
if(callback(collection[number]) === true){
delete collection[number];
}
}
};
return collection;
}
I know it could likely be much cleaner, but just for the sake of clarity, I wanted to show a solution that works.
I have created a not() function, which accepts a function and can be passed to a filter function:
// wraps passed function, so that when called,
// the return value will be negated
function not(a){
return function(){
return !a.apply(a, arguments);
}
}
Usage:
// some sample data
var animals = [
{name: 'Pete', type: 'fish'},
{name: 'Jim', type: 'fish'},
{name: 'Tom', type: 'cat'}
];
// a simple filter callback
var isCat = function(animal){
return animal.type === 'cat';
};
// gather all animals, which are not cats
var fishes = animals.filter(not(isCat));
I know it could likely be much cleaner
Just for the fun, here is how I alter the answer step by step to make it a bit cleaner.
Making it immutable
Changing the collection will change the original object, which is passed to the function,
since arrays and objects are reference types. To solve this, you can clone the collection and work
on that, or you can copy the elements, which are not rejected by the callback. I'm doing the latter.
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = [];
for (var i = 0; i < collection.length; i++) {
if(!callback(collection[i])){
ret.push(collection[i]);
}
}
} else {
ret = {};
for (number in collection){
if(!callback(collection[number])){
ret[number] = collection[number];
}
}
}
return ret;
}
Shortening with ES5
The loops' mechanics and the actual code done by the loop is entangled, we can have a much cleaner
code, if we stop concentrating on how to write a loop and let JS do it. For example: notice how
I refer to the individual elements of the array collection as value, instead of collection[i].
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = [];
collection.forEach(function(value){
if(!callback(value)){
ret.push(value);
}
});
} else {
ret = {};
Object.keys(collection).forEach(function(key){
var value = collection[key];
if(!callback(value)){
ret[key] = value;
}
});
}
return ret;
}
Changing if to filter()
Array.prototype.filter() is a bit more useful for us, than forEach, since in the core of the loop you can
simply return a truthy or falsy value and filter will handle collecting the data to a new array based on
that automatically for you.
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = collection.filter(function(value){
return !callback(value);
});
} else {
ret = {};
Object.keys(collection).filter(function(key){
return !callback(collection[key]);
}).forEach(function(key){
ret[key] = collection[key];
});
}
return ret;
}
Using reduce for objects
The goal would be to minimize functions, which go outside from their scope in order to work correctly.
In the objects part we can use Array.prototype.reduce() instead of forEach() and simply return it's
output directly to the ret value, when we are done, just as we did in the Array part with filter().
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = collection.filter(function(value){
return !callback(value);
});
} else {
ret = Object.keys(collection).filter(function(key){
return !callback(collection[key]);
}).reduce(function(obj, key){
obj[key] = collection[key];
return obj;
}, {});
}
return ret;
}
Shortening functions with ES6
Since we are already using Array.isArray(), which is an ES6 method, we can try using arrow functions
to compress anonymus functions.
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = collection.filter(value => !callback(value));
} else {
ret = Object.keys(collection)
.filter(key => !callback(collection[key]))
.reduce((obj, key) => {
obj[key] = collection[key];
return obj;
}, {})
;
}
return ret;
}
We don't need the ret variable
We previously removed the need to access the ret value in our logics, we can use a ternary operator
to directly return the value generated by the expressions.
function reject(collection, callback) {
return (
Array.isArray(collection)
? collection
.filter(value => !callback(value))
: Object.keys(collection)
.filter(key => !callback(collection[key]))
.reduce((obj, key) => {
obj[key] = collection[key];
return obj;
}, {})
)
}

js function always returns null

This function always returns null...
function find1(arry, uid){
arry.forEach(function(obj){
if(obj.uid === uid){
return obj;
}
});
return null;
}
but when I change it to below code, it works...
function find2(arry, uid){
var dt;
arry.forEach(function(obj){
if(obj.uid === uid){
dt = obj;
}
});
return dt;
}
var array = [
{uid:"name01"},
{uid:"name02"},
{uid:"name04"},
{uid:"name04"}
];
console.log('find1', find1(array, 'name02')); // console output: find1 null
console.log('find2', find2(array, 'name02')); // console output: find2 Object {uid: "name02"}
what am I doing wrong in find1?
In find1 method you are explicitly returning null. And in forEach loop on arry value, it does not return from the find1 method, it returns from the anonymous function the inside forEach for all the values in array.
In first example you return return from internal function, not from function 'find1'. The result returns from internal function to body of 'find1'. But 'find1' doesn't return this result.
To fix it you only need a variable in function 'find1'. Like so:
var result = array.forEach(...);
return result;
Or so:
var result = null;
arry.forEach(function(obj){
if(obj.uid === uid){
result = obj;
}
});
return result;

How do I allow an empty result in my filter?

I filter the elements of an array using this function:
function filter(arr, criteria) {
return arr.filter(function(obj) {
return Object.keys(criteria).every(function(c) {
return obj[c] == criteria[c];
});
});
}
var newarr = filter(arr, { desc: dv, aasc: av, rev: cv, flo: acv });
However, if the user doesn't input anything, then the results will return nothing. I am trying to code it so that if they choose nothing for that specific criteria, then it won't filter it.
Maybe I'm not getting it, but wouldn't you just check for empty values
function filter(arr, criteria) {
return arr.filter(function(obj) {
return Object.keys(criteria).every(function(c) {
return !(criteria[c]) || obj[c] == criteria[c];
});
});
}
You could just check the criteria for any value ?
function filter(arr, criteria) {
if(criteria)
{
return arr.filter(function(obj) {
return Object.keys(criteria).every(function(c) {
return obj[c] == criteria[c];
});
});
}
else
{
return error of some kind.
}
}
Or you could add a check for null input on the filter input...
I am trying to code it so that if they choose nothing for that specific criteria, then it won't filter it.
Then do that. Get the filters as an object from user inputs, or null if no filters are provided then just do something, like this:
var userFilters = ... // get filters
var newarr = userFilters ? filter(arr, userFilters) : arr;
If for some reason this doesn't work for you, you could always rewrite your filter method like this:
function filter(arr, criteria) {
if (criteria)
return arr.filter(function(obj) {
return Object.keys(criteria).every(function(c) {
return obj[c] == criteria[c];
});
});
else
return arr;
}
However, from your comments, it seems like what you mean is simply that if a value is not provided, you'd like to not apply that filter, but that other filters would still apply. In that case your current code works, just change how you're generating the filters object:
var userFilters = {};
if (dv) userFilters[desc] = dv;
if (av) userFilters[aasc] = av;
if (cv) userFilters[rev] = cv;
if (acv) userFilters[flo] = acv;
var newarr = filter(arr, userFilters);
This works because it only adds elements to the filter collection when they're specified. If no items are added, the then arr is returned unmodified.

Function with forEach returns undefined even with return statement

I'm just making a function for checking a value of something in my object array, but for some reason it keeps returning undefined. Why is that?
Demo: http://jsfiddle.net/cNYwz/1/
var data = [{
"Key": "1111-1111-1111",
"Email": "test#test.com"
}, {
"Key": "2222-2222-2222",
"Email": "test#boo.com"
}];
function getByKey(key) {
data.forEach(function (i, val) {
if (data[val].Key === key) {
return data[val].Key;
} else {
return "Couldn't find";
}
});
}
var asd = getByKey('1111-1111-1111');
console.log(asd);
In your function, you're returning from the function passed to forEach, not from getByKey.
You could adapt it like this :
function getByKey(key) {
var found = null;
data.forEach(function (val) {
if (val.Key === key) {
found = val;
}
});
return found;
}
But this would iterate over all elements, even if the item is immediately found. That's why you'd better use a simple for loop :
function getByKey(key) {
for (var i=0; i<data.length; i++) {
if (data[i].Key === key) {
return data[i];
}
}
}
Note that I also adapted your code to return the value, not the key. I suppose that was the intent. You might also have been confused with another iteration function : the first argument passed to the callback you give to forEach is the element of the array.
Your function getByKey has no return statement. The two returns are for the anonymous function used by forEach.
You're not returning anything to the outer scope, try this alternative:
function getByKey(key) {
var result = data.filter(function (i, val) {
return data[val].Key == key;
});
return result.length ? result : 'Not found';
}
Try storing the positive result as a variable, and then returning that variable (or a "Couldn't find" in case nothing is written) at the end of the function after the forEach loop.
function getByKey(key) {
var result;
data.forEach(function (val, i) {
if (data[val].Key === key) {
result = data[val].Key;
}
});
return result || "Couldn't find";
}
In addition to the ideas in the other answers, you're better off using Array.prototype.some, rather than forEach. That will let you stop when you find the first match:
function getByKey(key) {
var found = null;
data.some(function (val) {
if (val.Key === key) {
found = val;
return true; //stop iterating
}
});
return found;
}
You might also consider using filter, which can return an array containing just the objects where key matches:
function filter_array_by_key(key){
return data.filter(function(v){
return v.Key===key;
};
}
To get the first matching object, you can then use filter_array_by_key(key)[0], which will yield undefined if there was no match.

Categories

Resources