Given the following obj:
var inputMapping = {
nonNestedItem: "someItem here",
sections: {
general: "Some general section information"
}
};
I'm writing a function to get that data by passing in a string "nonNestedItem" or in the nested case "sections.general". I'm having to use an eval and I was wondering if there was maybe a better way to do this.
Here is what I have so far and it works okay. But improve!
function getNode(name) {
var n = name.split(".");
if (n.length === 1) {
n = name[0];
} else {
var isValid = true,
evalStr = 'inputMapping';
for (var i=0;i<n.length;i++) {
evalStr += '["'+ n[i] +'"]';
if (eval(evalStr) === undefined) {
isValid = false;
break;
}
}
if (isValid) {
// Do something like return the value
}
}
}
Linky to Jsbin
You can use Array.prototype.reduce function like this
var accessString = "sections.general";
console.log(accessString.split(".").reduce(function(previous, current) {
return previous[current];
}, inputMapping));
Output
Some general section information
If your environment doesn't support reduce, you can use this recursive version
function getNestedItem(currentObject, listOfKeys) {
if (listOfKeys.length === 0 || !currentObject) {
return currentObject;
}
return getNestedItem(currentObject[listOfKeys[0]], listOfKeys.slice(1));
}
console.log(getNestedItem(inputMapping, "sections.general".split(".")));
You don't need to use eval() here. You can just use [] to get values from an object. Use a temp object to hold the current value, then update it each time you need the next key.
function getNode(mapping, name) {
var n = name.split(".");
if (n.length === 1) {
return mapping[name];
} else {
var tmp = mapping;
for (var i = 0; i < n.length; i++) {
tmp = tmp[n[i]];
}
return tmp;
}
}
Related
My javascript object looks like the example below, I am wondering how I should write a swap function to change the element position in the object. For example, I want to swap two elements from position 1 to 2 and 2 to 1.
{
element_name_1 : {
//.. data
}
element_name_2 : {
//.. data
}
element_name_3 : {
//.. data
}
element_name_4 : {
//.. data
}
}
Now I want to swap element_name_2 with element_name_1.
As Miles points out, your code is probably broken and should use an array. I wouldn't use it, nor is it tested, but it is possible.
var data = {
element_name_1: {},
element_name_2: {},
element_name_3: {},
element_name_4: {}
}
console.log(data);
var swap = function(object, key1, key2) {
// Get index of the properties
var pos1 = Object.keys(object).findIndex(x => {
return x === key1
});
var pos2 = Object.keys(object).findIndex(x => {
return x === key2
});
// Create new object linearly with the properties swapped
var newObject = {};
Object.keys(data).forEach((key, idx) => {
if (idx === pos1)
newObject[key2] = object[key2];
else if (idx === pos2)
newObject[key1] = object[key1];
else
newObject[key] = object[key];
});
return newObject;
}
console.log(swap(data, "element_name_1", "element_name_2"));
Have a look at the code, may this solve the problem
function swapFunction(source, destination) {
var tempValu,
sourceIndex;
for ( i = 0; i < Arry.length; i++) {
for (var key in Arry[i]) {
Ti.API.info('key : ' + key);
if (source == key) {
tempValu = Arry[i];
sourceIndex = i;
}
if (destination == key) {
Arry[sourceIndex] = Arry[i];
Arry[i] = tempValu;
return Arry;
}
}
}
}
JSON.stringify(swapFunction("key_1", "key_3")); // [{"key_3":"value_3"},{"key_2":"value_2"},{"key_1":"value_1"},{"key_4":"value_4"},{"key_5":"value_5"}]
Let me know if this works.
Good Luck & Cheers
Ashish Sebastian
I'd like to extend javascript to add custom type checking.
e.g.
function test(welcome:string, num:integer:non-zero) {
console.log(welcome + num)
}
which would compile into:
function test(welcome, num) {
if(Object.prototype.toString.call(welcome) !== "[object String]") {
throw new Error('welcome must be a string')
}
if (!Number.isInteger(num)) {
throw new Error('num must be an integer')
}
console.log(welcome + num)
}
What's the most straightforward way of doing this?
So far i've looked at:
sweet.js (online documentation looks out of date as I think it's going through some sort of internal rewrite)
esprima and escodegen (not sure where to start)
manually parsing using regular expressons
After evaluating all the various options, using sweet.js appears to be the best solution. It's still fairly difficult to get working (and I am probably doing stuff the wrong way) but just in case someone want's to do something similar this here was my solution.
'use strict'
syntax function = function(ctx) {
let funcName = ctx.next().value;
let funcParams = ctx.next().value;
let funcBody = ctx.next().value;
//produce the normal params array
var normalParams = produceNormalParams(funcParams)
//produce the checks
var paramChecks = produceParamChecks(funcParams)
//produce the original funcBody code
//put them together as the final result
var params = ctx.contextify(funcParams)
var paramsArray = []
for (let stx of params) {
paramsArray.push(stx)
}
var inner = #``
var innerStuff = ctx.contextify(funcBody)
for (let item of innerStuff) {
inner = inner.concat(#`${item}`)
}
var result = #`function ${funcName} ${normalParams} {
${paramChecks}
${inner}
}`
return result
function extractParamsAndParamChecks(paramsToken) {
var paramsContext = ctx.contextify(paramsToken)
//extracts the actual parameters
var paramsArray = []
var i = 0;
var firstItembyComma = true
for (let paramItem of paramsContext) {
if (firstItembyComma) {
paramsArray.push({
param: paramItem,
checks: []
})
firstItembyComma = false
}
if (paramItem.value.token.value === ',') {
firstItembyComma = true
i++
} else {
paramsArray[i].checks.push(paramItem.value.token.value)
}
}
for (var i = 0; i < paramsArray.length; i++) {
var checks = paramsArray[i].checks.join('').split(':')
checks.splice(0, 1)
paramsArray[i].checks = checks
}
return paramsArray
}
function produceNormalParams(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
//Produces the final params #string
var inner = #``
var first = true
for (let item of paramsArray) {
if (first === true) {
inner = inner.concat(#`${item.param}`)
} else {
inner = inner.concat(#`,${item.param}`)
}
}
return #`(${inner})`
}
function produceParamChecks(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
var result = #``
for (let paramObject of paramsArray) {
var tests = produceChecks(paramObject)
result = result.concat(#`${tests}`)
}
return result
}
function produceChecks(paramObject) {
var paramToken = paramObject.param
var itemType = paramObject.checks[0]
var checks = paramObject.checks
if (itemType === undefined) return #``
if (itemType === 'array') {
return #`if (Object.prototype.toString.call(${paramToken}) !== "[object Array]") throw new Error('Must be array:' + ${paramToken})`
else {
throw new Error('item type not recognised: ' + itemType)
}
}
}
I have been trying to translate my code from es6 to es5 because of some framework restrictions at my work... Although I have been quite struggling to locate what the problem is. For some reason the code does not work quite the same, and there is no errors either ...
Can someone tell me If I have translated properly ?
This is the ES6 code :
function filterFunction(items, filters, stringFields = ['Title', 'Description'], angular = false) {
// Filter by the keys of the filters parameter
const filterKeys = Object.keys(filters);
// Set up a mutable filtered object with items
let filtered;
// Angular doesn't like deep clones... *sigh*
if (angular) {
filtered = items;
} else {
filtered = _.cloneDeep(items);
}
// For each key in the supplied filters
for (let key of filterKeys) {
if (key !== 'TextInput') {
filtered = filtered.filter(item => {
// Make sure we have something to filter by...
if (filters[key].length !== 0) {
return _.intersection(filters[key], item[key]).length >= 1;
}
return true;
});
}
// If we're at TextInput, handle things differently
else if (key === 'TextInput') {
filtered = filtered.filter(item => {
let searchString = "";
// For each field specified in the strings array, build a string to search through
for (let field of stringFields) {
// Handle arrays differently
if (!Array.isArray(item[field])) {
searchString += `${item[field]} `.toLowerCase();
} else {
searchString += item[field].join(' ').toLowerCase();
}
}
// Return the item if the string matches our input
return searchString.indexOf(filters[key].toLowerCase()) !== -1;
});
}
}
return filtered;
}
And this is the code I translated that partially 99% work ..
function filterFunction(items, filters, stringFields, angular) {
// Filter by the keys of the filters parameter
var filterKeys = Object.keys(filters);
// Set up a mutable filtered object with items
var filtered;
// Angular doesn't like deep clones... *sigh*
if (angular) {
filtered = items;
} else {
filtered = _.cloneDeep(items);
}
// For each key in the supplied filters
for (var key = 0 ; key < filterKeys.length ; key ++) {
if (filterKeys[key] !== 'TextInput') {
filtered = filtered.filter( function(item) {
// Make sure we have something to filter by...
if (filters[filterKeys[key]].length !== 0) {
return _.intersection(filters[filterKeys[key]], item[filterKeys[key]]).length >= 1;
}
return true;
});
}
// If we're at TextInput, handle things differently
else if (filterKeys[key] === 'TextInput') {
filtered = filtered.filter(function(item) {
var searchString = "";
// For each field specified in the strings array, build a string to search through
for (var field = 0; field < stringFields.length; field ++) {
// Handle arrays differently
console.log(field);
if (!Array.isArray(item[stringFields[field]])) {
searchString += item[stringFields[field]] + ' '.toLowerCase();
} else {
searchString += item[stringFields[field]].join(' ').toLowerCase();
}
}
// Return the item if the string matches our input
return searchString.indexOf(filters[filterKeys[key]].toLowerCase()) !== -1;
});
}
}
return filtered;
}
These two lines
searchString += `${item[field]} `.toLowerCase();
searchString += item[stringFields[field]] + ' '.toLowerCase();
are not equivalent indeed. To apply the toLowerCase method on all parts of the string, you'll need to wrap the ES5 concatenation in parenthesis:
searchString += (item[stringFields[field]] + ' ').toLowerCase();
or, as blanks cannot be lowercased anyway, just use
searchString += item[stringFields[field]].toLowerCase() + ' ';
Here is a translated code from babeljs itself, as commented above.
'use strict';
function filterFunction(items, filters) {
var stringFields = arguments.length <= 2 || arguments[2] === undefined ? ['Title', 'Description'] : arguments[2];
var angular = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
// Filter by the keys of the filters parameter
var filterKeys = Object.keys(filters);
// Set up a mutable filtered object with items
var filtered = void 0;
// Angular doesn't like deep clones... *sigh*
if (angular) {
filtered = items;
} else {
filtered = _.cloneDeep(items);
}
// For each key in the supplied filters
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
var _loop = function _loop() {
var key = _step.value;
if (key !== 'TextInput') {
filtered = filtered.filter(function (item) {
// Make sure we have something to filter by...
if (filters[key].length !== 0) {
return _.intersection(filters[key], item[key]).length >= 1;
}
return true;
});
}
// If we're at TextInput, handle things differently
else if (key === 'TextInput') {
filtered = filtered.filter(function (item) {
var searchString = "";
// For each field specified in the strings array, build a string to search through
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = stringFields[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var field = _step2.value;
// Handle arrays differently
if (!Array.isArray(item[field])) {
searchString += (item[field] + ' ').toLowerCase();
} else {
searchString += item[field].join(' ').toLowerCase();
}
}
// Return the item if the string matches our input
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
return searchString.indexOf(filters[key].toLowerCase()) !== -1;
});
}
};
for (var _iterator = filterKeys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
_loop();
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return filtered;
}
p.s. Or there is a better way to use babeljs directly without manually converting it.
I have a function that need to return a value from a array.
function getValue(key) : value
function getValue(key) {
var result = null;
$scope.config.forEach(function(element) {
if(element.app_key == "search_result_limit")
result = element.app_value;
break;
});
return result;
}
i am trying to get this value but found undefined.
Please help.
you should be using a filter method:
$scope.config.filter(function(element) {
return element.app_key == "search_result_limit";
});
considering that your $scope.config is an array of objects...
Modern JS has Array.prototype.find:
function getValue(key) {
var obj = $scope.config.find(function(e) {
return e.app_key === key;
});
return obj ? obj.app_value : null;
}
or in ES6 syntax:
function getValue(key) {
var obj = $scope.config.find(e => e.app_key === key);
return obj ? obj.app_value : null;
}
If you don't have ES6, you're better off just using a plain for loop:
function getValue(key) {
for (var a = $scope.config, i = 0, n = a.length; i < n; ++i) {
if (a[i].app_key === key) {
return a[i].app_value;
}
}
return null;
}
you need to put two statement inside if statement in a block.
Use this code, this might work :-
function getValue(key) : value
function getValue(key) {
for ( var a = $scope.config, i=0, n = a.length; i< n ; i++){
if (a[i].app_key === key) {
return a[i].app_value;
}
}
return null;
}
use this code
function getValue(key) {
var result= $scope.config.filter(function(item) {
return item.app_key == key;
});
return result.length>0?result[0].app_value:null;
}
I am trying to figure out a javascript function that will help resolve this test. I need to be able to determine if the string of words (var matches) that is given is an anagram of the word that I am running through (var subject). In this case there would not be a match. Any and all help will be greatly appreciated. Thank you in advance!
var anagram = require('./anagram');
describe('Anagram', function() {
it("no matches",function() {
var subject = anagram("diaper");
var matches = subject.matches([ "hello", "world", "zombies", "pants"]);
expect(matches).toEqual([]);
});
});
This is what I have so far:
for (var i = 0; i < matches.length; i++) {
if (subject.length != matches[i].length) {
return false
} else if (subject.length == matches[i].length){
var anagram = function(subject, matches) {
return subject.split("").sort("").join("") === matches[i].split("").sort("").join("");
};
}
Here is the fiddle:
http://jsfiddle.net/hn8r4v3u/2/
I alphabetized the letters within the word, as you were doing, in a function.
function getAlphaSortedWord(word) {
var baseWordCharArray = word.split("");
baseWordCharArray.sort();
return baseWordCharArray.join("");
}
The code has a set up:
var baseWord = getAlphaSortedWord("bob");
var thingsToCheck = ["obb", "2", "bob", "", "bo", "ob"];
And then solves it two ways, once with filter and once without it.
var matches = _.filter(thingsToCheck, function (str) {
return (baseWord === getAlphaSortedWord(str));
});
var matches2 = [];
for (index = 0; index < thingsToCheck.length; index++) {
if (baseWord === getAlphaSortedWord(thingsToCheck[index])) {
matches2.push(thingsToCheck[index]);
}
}
You should be able to use these to tie in with your real data for the test to pass.
NOTE, I would add some sanity for "is string" to my function if this is going to be production code.
Found here and it works: https://gist.github.com/AlbertoElias/10005056
function areAnagrams(a, b) {
var c = false;
if (a.length !== b.length) {
return c;
}
var hashMap = {};
var char;
var i;
for (i=0;i<a.length;i++) {
char = a[i];
hashMap[char] = hashMap[char] !== undefined ? hashMap[char]+1 : 1;
}
for (i=0;i<b.length;i++) {
char = b[i];
if (hashMap[char] !== undefined) {
if (hashMap[char] > 1) {
hashMap[char]--;
} else {
delete hashMap[char];
}
} else {
return c;
}
}
if (Object.keys(hashMap).length === 0) c = true;
return c;
}