Quickest way to check which property inside an object holds value? - javascript

Probably a dumb question, Assume i have an object like below,
{"Country":"country","Continent":"continent","Province":"","District":"","State":"state","City":""}"
What is the quickest way to check which properties hold the values in the above object without using a loop?
while doing this inside a for loop is working,
if(typeof someUndefVar == whatever) -- works
EXPECTED OUTPUT:
Country, Continent and State

var a = {"Country":"country","Continent":"continent","Province":"","District":"","State":"state","City":""};
Object.keys(a).filter( prop => a[prop] );
It also depends on how you want to handle the 0, null, undefined values.

You might do as follows;
var obj = {"Country":"country","Continent":"continent","Province":"","District":"","State":"state","City":""};
for (var prop in obj) !!obj[prop] && console.log(prop);

You need to use the object.keys() function,
Like this,
var jsonData= {"Country":"country","Continent":"continent","Province":"","District":"","State":"state","City":""};
console.log(Object.keys(jsonData));
Hope this helps!

Sorry you said no loop and I got excited and I was bored and I got functional.
First I shortened some names and I guess they are kind of self explanatory:
head: first element of an array
tail: all the elements but the first
isEmpty: check if an array has length 0
transform does the actual work using a callback, it stops if the keys array is empty if not goes recursive.
var data = {"Country":"country","Continent":"continent","Province":"","District":"","State":"state","City":""}
// plumbing
var head = (a) => a[0]
var tail = (a) => a.slice(1)
var isEmpty = (a) => a.length === 0
// actual stuff happens here
var transform = (obj, callback) => {
var withval = (keys) => {
if(isEmpty(keys)) return
var p = head(keys)
if(!!obj[p]) callback(p)
withval(tail(keys))
}
withval(Object.keys(obj))
}
// Logs
var log = (p) => console.log(p)
transform(data, log)
// Array making
var arr = [];
var toArr = (p) => arr.push(p)
transform(data, toArr)
console.log(arr)
// Object making
var obj = {}
var toObj = (p) => obj[p] = data[p]
transform(data, toObj)
console.log(obj)
Outcome:
Country
Continent
State
[ 'Country', 'Continent', 'State' ]
{ Country: 'country', Continent: 'continent', State: 'state' }

Related

Oh man groupBy() and arrayToObject() are breaking me

So I have been working on some extra credit for my classes. I am very new to programming and have already sought help for this same assignment, I started rolling through and now am absolutely lost.
I need to define the two functions groupBy() and arrayToObect() as asked in the below test.
I'm not necessarily looking for the answer but if someone could help point me in the right direction that would be awesome.
What I have deduced is as follows:
I need to be using the spread operator ...
I need to create a newObj = {}
a. and somehow push the element derived from the array into the obj
I need to take the individual values of the array and assign them as keys, with the variances as the properties of the key.
Bracket notation
I have been racking my brain for hours on this now and could really use some guidance!
describe('groupBy', function () {
const input = [4.2, 6.1, 6.3]
const result = groupBy(input, (el) => Math.floor(el))
it('returns an object', function () {
expect(result).to.be.an('object')
})
it('group array items together based on the callback return value', function () {
expect(result).to.be.eql({
4: [4.2],
6: [6.1, 6.3],
})
})
})
describe('arrayToObject', function () {
const input = ['cat', 'dog', 'bird']
const result = arrayToObject(input, (word) => word + 's')
it('returns an object', function () {
expect(result).to.be.an('object')
})
it('object has original array elements as keys and the result of the callback as values', function () {
expect(result).to.be.eql({
cat: 'cats',
dog: 'dogs',
bird: 'birds',
})
})
})
})
groupBy
Write a function called groupBy which takes an array and a callback. The function should return an object. Each return value of the callback should be a key of the object and the values should be the input element with which the callback was called.
arrayToObject
Write a function called arrayToObject which takes an array and a callback. The function should return an object. Each element of the input array should be a key of the returned object and the output from the callback with an element passed in as the corresponding value.
These questions have been answered a million times on stackoverflow. Essentially what you want to be doing here is using the common js array functions map, filter, reduce, flatten, ..., and think about how your problem can be expressed in terms of those.
A lot of real world code is transforming data like this, so it's good to be comfortable doing it.
Also realize that spread syntax copies the entire object which can be pretty inefficient. JavaScript doesn't have persistent data structures! It's usually better to just mutate — as long as your code is what "owns" the object.
const groupBy = (elts, keyfn) =>
elts.reduce((m, elt) => {
const key = keyfn(elt);
m[key] = m[key] || [];
m[key].push(elt);
return m;
}, {});
const arrayToObject = (elts, fn) =>
elts.reduce(
(obj, elt) => Object.assign(obj, { [elt]: fn(elt) }),
{},
);
I figured it out using a for loop!!
function groupBy(arr, callback) {
const newObj = {}
for (let i = 0; i < arr.length; i++) {
if (callback(arr[i])) {
const key = callback(arr[i])
newObj[key] = newObj[key] || []
newObj[key].push(arr[i])
}
}
return newObj
}
function arrayToObject(arr, callback) {
const obj = {}
for (let i = 0; i < arr.length; i++) {
if (callback(arr[i])) {
const key = callback(arr[i])
obj[arr[i]] = obj[key] || callback(arr[i])
}
}
return obj
}

ES6 Filtering objects by unique attribute

I've an array of errors, each error has a non-unique param attribute.
I'd like to filter the array based on whether the param has been seen before.
Something like this:
const filteredErrors = [];
let params = [];
for(let x = 0; x < errors.length; x++) {
if(!params.includes(errors[x].param)) {
params.push(errors[x].param);
filteredErrors.push(errors[x]);
}
}
But I've no idea how to do this in ES6.
I can get the unique params const filteredParams = Array.from(new Set(errors.map(error => error.param)));
but not the objects themselves.
Pretty sure this is just a weakness in my understanding of higher order functions, but I just can't grasp it
You could destrucure param, check against params and add the value to params and return true for getting the object as filtering result.
As result you get an array of first found errors of the same type.
const
params = [],
filteredErrors = errors.filter(({ param }) =>
!params.includes(param) && params.push(param));
Instead of an array you can make use of an object to keep a map of existing values and make use of filter function
let params = {};
const filteredErrors = errors.filter(error => {
if(params[error.param]) return false;
params[error.param] = true;
return true;
});
i'd probably do it like this with a reduce and no need for outside parameters:
const filteredErrors = Object.values(
errors.reduce((acc, val) => {
if (!acc[val.param]) {
acc[val.param] = val;
}
return acc;
}, {}))
basically convert it into an object keyed by the param with the object as values, only setting the key if it hasn't been set before, then back into an array of the values.
generalized like so
function uniqueBy(array, prop) {
return Object.values(
array.reduce((acc, val) => {
if (!acc[val[prop]]) {
acc[val[prop]] = val;
}
return acc;
}, {}))
}
then just do:
const filteredErrors = uniqueBy(errors, 'param');
If your param has a flag identifier if this param has been seen before then you can simply do this.
const filteredErrors = errors.filter(({ param }) => param.seen === true);
OR
const filteredErrors = errors.filter((error) => error.param.seen);
errors should be an array of objects.
where param is one of the fields of the element of array errors and seen is one of the fields of param object.
You can do it by using Array.prototype.reduce. You need to iterate through the objects in the array and keep the found params in a Set if it is not already there.
The Set.prototype.has will let you find that out. If it is not present in the Set you add it both in the Set instance and the final accumulated array, so that in the next iteration if the param is present in your Set you don't include that object:
const errors = [{param: 1, val: "err1"}, {param: 2, val: "err2"}, {param: 3, val: "err3"}, {param: 2, val: "err4"}, {param: 1, val: "err5"}];
const { filteredParams } = errors.reduce((acc, e) => {
!acc.foundParams.has(e.param) && (acc.foundParams.add(e.param) &&
acc.filteredParams.push(e));
return acc;
}, {foundParams: new Set(), filteredParams: []});
console.log(filteredParams);

Javascript - map value to keys (reverse object mapping)

I want to reverse the mapping of an object (which might have duplicate values). Example:
const city2country = {
'Amsterdam': 'Netherlands',
'Rotterdam': 'Netherlands',
'Paris': 'France'
};
reverseMapping(city2country) Should output:
{
'Netherlands': ['Amsterdam', 'Rotterdam'],
'France': ['Paris']
}
I've come up with the following, naive solution:
const reverseMapping = (obj) => {
const reversed = {};
Object.keys(obj).forEach((key) => {
reversed[obj[key]] = reversed[obj[key]] || [];
reversed[obj[key]].push(key);
});
return reversed;
};
But I'm pretty sure there is a neater, shorter way, preferably prototyped so I could simply do:
const country2cities = city2country.reverse();
You could use Object.assign, while respecting the given array of the inserted values.
const city2country = { Amsterdam: 'Netherlands', Rotterdam: 'Netherlands', Paris: 'France' };
const reverseMapping = o => Object.keys(o).reduce((r, k) =>
Object.assign(r, { [o[k]]: (r[o[k]] || []).concat(k) }), {})
console.log(reverseMapping(city2country));
There is no such built-in function in JavaScript. Your code looks fine, but given that there are so many edge cases here that could wrong, I'd suggesting using invertBy from lodash, which does exactly what you describe.
Example
var object = { 'a': 1, 'b': 2, 'c': 1 };
_.invertBy(object);
// => { '1': ['a', 'c'], '2': ['b'] }
You can use something like this to get raid of duplicates first :
function removeDuplicates(arr, key) {
if (!(arr instanceof Array) || key && typeof key !== 'string') {
return false;
}
if (key && typeof key === 'string') {
return arr.filter((obj, index, arr) => {
return arr.map(mapObj => mapObj[key]).indexOf(obj[key]) === index;
});
} else {
return arr.filter(function(item, index, arr) {
return arr.indexOf(item) == index;
});
}
}
and then use this to make it reverse :
function reverseMapping(obj){
var ret = {};
for(var key in obj){
ret[obj[key]] = key;
}
return ret;
}
You could try getting an array of values and an array of keys from the current object, and setup a new object to hold the result. Then, as you loop through the array of values -
if the object already has this value as the key, like Netherlands, you create a new array, fetch the already existing value (ex: Rotterdam), and add this and the new value (Amsterdam) to the array, and set up this array as the new value for the Netherlands key.
if the current value doesn't exist in the object, set it up as a new string, ex: France is the key and Paris is the value.
Code -
const city2country = {
'Amsterdam': 'Netherlands',
'Rotterdam': 'Netherlands',
'Paris': 'France',
};
function reverseMapping(obj) {
let values = Object.values(obj);
let keys = Object.keys(obj);
let result = {}
values.forEach((value, index) => {
if(!result.hasOwnProperty(value)) {
// create new entry
result[value] = keys[index];
}
else {
// duplicate property, create array
let temp = [];
// get first value
temp.push(result[value]);
// add second value
temp.push(keys[index]);
// set value
result[value] = temp;
}
});
console.log(result);
return result;
}
reverseMapping(city2country)
The benefit here is - it adjusts to the structure of your current object - Netherlands being the repeated values, gets an array as it's value in the new object, while France gets a string value Paris as it's property. Of course, it should be very easy to change this.
Note - Object.values() might not be supported across older browsers.
You could use reduce to save the declaration line reduce.
Abusing && to check if the map[object[key]] is defined first before using Array.concat.
It's shorter, but is it simpler? Probably not, but a bit of fun ;)
const reverseMapping = (object) =>
Object.keys(object).reduce((map, key) => {
map[object[key]] = map[object[key]] && map[object[key]].concat(key) || [key]
return map;
}, {});
#Nina Scholz answer works well for this exact question. :thumbsup:
But if you don't need to keep both values for the Netherlands key ("Netherlands": ["Amsterdam", "Rotterdam"]), then this is a little bit shorter and simpler to read:
const city2country = { Amsterdam: 'Netherlands', Rotterdam: 'Netherlands', Paris: 'France' };
console.log(
Object.entries(city2country).reduce((obj, item) => (obj[item[1]] = item[0]) && obj, {})
);
// outputs `{Netherlands: "Rotterdam", France: "Paris"}`

Unexpected behavior using ko.mapping.fromJS

I am experimenting with ko.mapping.fromJS
(http://knockoutjs.com/documentation/plugins-mapping.html)
Given the code below, and expectations, could someone explain to me why the actual output differs?
var obj = { name: "frederick", minions: [{id:1, name:'Alice'},{id:2, name:'Bob'}] }
var model = {
'minions': {
key: function(data) { return ko.utils.unwrapObservable(data.id); }
}
}
var vm = ko.mapping.fromJS(obj, model);
vm.name.subscribe(x=>console.log("Changed name"));
vm.minions.subscribe(x => console.log("changed minions"));
vm.minions()[0].id.subscribe(x => console.log("changed id [0]"));
vm.minions()[0].name.subscribe(x => console.log("changed name [0]"));
vm.minions()[1].id.subscribe(x => console.log("changed id [1]"));
vm.minions()[1].name.subscribe(x => console.log("changed name [1]"));
obj.minions[0].name = 'Charlie';
ko.mapping.fromJS(obj, vm);
Expected logging:
changed name [0]
Actual logging:
changed name [0]
changed minions
Question:
Since there are no records added or removed to the array, why do I see "Changed minions"? Are these events always bubbled up, or only in case of arrays and direct child objects (/rows)?
(Or did I make a mistake in the model? Can I 'fix' this?)
What's happening under the hood is this:
var a = { a: 1 };
var b = { b: 2 };
var arr1 = [a, b];
var obsArray = ko.observableArray(arr2);
obsArray.subscribe(_ => console.log("change"));
var arr2 = [a, b];
// Logs change, since
// arr1 !== arr2
// even though:
// arr1[0] === arr2[0] and
// arr1[1] === arr2[1]
obsArray([a, b]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
I'd say the easiest way to counter this issue is to check for "equality" in your subscription method, but that's mainly because I don't know the mapping plugin very well...
How I'd solve it:
Create a helper function that takes an observableArray and a callback function.
The helper defines an equalityCheck that takes two arrays as input and returns true or false
The helper creates a regular subscription to the observable but only calls the passed callback if the equality check passes.
To compare the new value that is passed to the knockout subscription with the previous one, it needs to store a reference to the previous value.
This means your syntax will change from:
vm.minions.subscribe(x => console.log("changed minions"));
to:
customSubscribe(vm.minions, x => console.log("changed minions"));
There might also be a way to fix this via the plugin or via an extender...
const obsArraySubThatChecksInner = (arr, cb) => {
let prevValues = arr();
// Check if items are equal without checking the array reference
const equalityCheck = (arr1, arr2) => {
return arr1.length === arr2.length &&
arr1.every((item, i) => item === arr2[i]);
}
return arr.subscribe(newValues => {
// Make a copy to prevent mutations in cb
const newArray = newValues.slice();
if (!equalityCheck(prevValues, newValues)) {
cb(newValues);
};
prevValues = newArray;
});
};
var obsArr = ko.observableArray([1, 2, 3]);
var regularSub = obsArr.subscribe(vals => console.log("Regular sub:", JSON.stringify(vals)));
var specialSub = obsArraySubThatChecksInner(
obsArr, vals => console.log("Special sub:", JSON.stringify(vals))
);
// Is "similar", so will only trigger the regular sub.
obsArr([1, 2, 3]);
// Is different, so will trigger both
obsArr([1, 2]);
obsArr.push(3);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
With your data in a fiddle: https://jsfiddle.net/nk074pb3/

Getting the first index of an object

Consider:
var object = {
foo: {},
bar: {},
baz: {}
}
How would I do this:
var first = object[0];
console.log(first);
Obviously, that doesn’t work because the first index is named foo,
not 0.
console.log(object['foo']);
works, but I don’t know it’s named foo. It could be named anything. I just want the first.
Just for fun this works in JS 1.8.5
var obj = {a: 1, b: 2, c: 3};
Object.keys(obj)[0]; // "a"
This matches the same order that you would see doing
for (o in obj) { ... }
If you want something concise try:
for (first in obj) break;
alert(first);
wrapped as a function:
function first(obj) {
for (var a in obj) return a;
}
they're not really ordered, but you can do:
var first;
for (var i in obj) {
if (obj.hasOwnProperty(i) && typeof(i) !== 'function') {
first = obj[i];
break;
}
}
the .hasOwnProperty() is important to ignore prototyped objects.
This will not give you the first one as javascript objects are unordered, however this is fine in some cases.
myObject[Object.keys(myObject)[0]]
If the order of the objects is significant, you should revise your JSON schema to store the objects in an array:
[
{"name":"foo", ...},
{"name":"bar", ...},
{"name":"baz", ...}
]
or maybe:
[
["foo", {}],
["bar", {}],
["baz", {}]
]
As Ben Alpert points out, properties of Javascript objects are unordered, and your code is broken if you expect them to enumerate in the same order that they are specified in the object literal—there is no "first" property.
for first key of object you can use
console.log(Object.keys(object)[0]);//print key's name
for value
console.log(object[Object.keys(object)[0]]);//print key's value
There is no way to get the first element, seeing as "hashes" (objects) in JavaScript have unordered properties. Your best bet is to store the keys in an array:
var keys = ["foo", "bar", "baz"];
Then use that to get the proper value:
object[keys[0]]
ES6
const [first] = Object.keys(obj)
Using underscore you can use _.pairs to get the first object entry as a key value pair as follows:
_.pairs(obj)[0]
Then the key would be available with a further [0] subscript, the value with [1]
I had the same problem yesterday. I solved it like this:
var obj = {
foo:{},
bar:{},
baz:{}
},
first = null,
key = null;
for (var key in obj) {
first = obj[key];
if(typeof(first) !== 'function') {
break;
}
}
// first is the first enumerated property, and key it's corresponding key.
Not the most elegant solution, and I am pretty sure that it may yield different results in different browsers (i.e. the specs says that enumeration is not required to enumerate the properties in the same order as they were defined). However, I only had a single property in my object so that was a non-issue. I just needed the first key.
You could do something like this:
var object = {
foo:{a:'first'},
bar:{},
baz:{}
}
function getAttributeByIndex(obj, index){
var i = 0;
for (var attr in obj){
if (index === i){
return obj[attr];
}
i++;
}
return null;
}
var first = getAttributeByIndex(object, 0); // returns the value of the
// first (0 index) attribute
// of the object ( {a:'first'} )
To get the first key of your object
const myObject = {
'foo1': { name: 'myNam1' },
'foo2': { name: 'myNam2' }
}
const result = Object.keys(myObject)[0];
// result will return 'foo1'
Based on CMS answer. I don't get the value directly, instead I take the key at its index and use this to get the value:
Object.keyAt = function(obj, index) {
var i = 0;
for (var key in obj) {
if ((index || 0) === i++) return key;
}
};
var obj = {
foo: '1st',
bar: '2nd',
baz: '3rd'
};
var key = Object.keyAt(obj, 1);
var val = obj[key];
console.log(key); // => 'bar'
console.log(val); // => '2nd'
My solution:
Object.prototype.__index = function(index)
{
var i = -1;
for (var key in this)
{
if (this.hasOwnProperty(key) && typeof(this[key])!=='function')
++i;
if (i >= index)
return this[key];
}
return null;
}
aObj = {'jack':3, 'peter':4, '5':'col', 'kk':function(){alert('hell');}, 'till':'ding'};
alert(aObj.__index(4));

Categories