Related
I'm writing a piece of code in JavaScript for modern browser. I'm not using lodash or underscore as we want to keep the library as small as possible.
For example,
If the url comes like this. http://something.com/#hash=value
And the app is configured to capture key hash then the result would be this. Nothing fancy. I was just wondering if there's a better way or simple way to do this.
{
'hash': 'value'
}
The code
var config = Object.assign({}, {
capturedHashParams: ['hash']
});
var hashValue = '#hash=value1'.substr(1);
var capturedHashParams = {};
if (config.capturedHashParams && Array.isArray(config.capturedHashParams)) {
var splitedHash = hashValue.split('=');
if (splitedHash.length > 0) {
var key = splitedHash[0] || '';
var value = splitedHash[1] || '';
if (key && value) {
config.capturedHashParams.forEach(function(hp) {
if (hp.toLowerCase().indexOf(key.toLowerCase()) > -1) {
capturedHashParams[key] = value;
}
});
}
}
}
console.log(capturedHashParams);
https://jsfiddle.net/c92p0rfm/2/
i think you are watching for something like this:
var output = [];
var hashString = window.location.hash;
var hashArray = hash.split('&');
for(var index = 0; index < hashArray.length; index++){
var text = hashArray[index];
var tempArray= text.split('=');
var object = {
id: tempArray[0],
value: tempArray[1]
};
output[index] = object;
}
now output looks like this:
[
{
id: "hash",
value: "value"
}
]
Your question is somewhat ambiguous. It appears that you want to extract key/value pairs from an url hash and return as a JavaScript object. I am bit uncertain about whether you want to extract all key/value pairs or only those provided in a config object. I am also a bit uncertain as to whether you want a solution within a strict functional programming paradigm, or just a plain solution with a small code footprint. I will assume the latter.
A straightforward approach to capture all key/value pairs:
var url = 'http://something.com/#hash=value&anotherHash=value';
//extract key=value pairs from url
var params = url.split('#').pop().split('&');
//assign to data object
for(var data = {}, i = 0, temp; i < params.length; i++){
// extract array [key, value]
temp = params[i].split('=');
// assign to data object
data[temp[0]] = temp[1];
}
console.log(data);
A more compact solution to do the same with .reduce():
var url = 'http://something.com/#hash=value&anotherHash=value';
var data = url
.split('#')
.pop()
.split('&')
.reduce(function(obj, keyval){
keyval = keyval.split('=');
obj[keyval[0]] = keyval[1];
return obj;
}, {});
console.log(data)
If you want to configure which keys to extract:
var url = 'http://something.com/#hash=value&anotherHash=value¬ThisHash=value';
//config object
var keysToCapture = [
'hash',
'anotherHash'
];
var data = url
.split('#')
.pop()
.split('&')
.reduce(function(obj, keyval){
keyval = keyval.split('=');
if(keysToCapture.indexOf(keyval[0]) > -1){
obj[keyval[0]] = keyval[1];
}
return obj;
}, {});
console.log(data)
Which you could capture in a reusable function like this:
function extractParamsObject(url, keysToCapture){
return url
.split('#')
.pop() //hash/fragment: everything after the last #
.split('&')
.reduce(function(obj, keyval){
keyval = keyval.split('=');
if(keysToCapture.indexOf(keyval[0]) > -1){
obj[keyval[0]] = keyval[1];
}
return obj;
}, {});
}
console.log(extractParamsObject(
'http://something.com/#hash=value&anotherHash=value¬ThisHash=value',
['hash', 'anotherHash']
));
I have array object(x) that stores json (key,value) objects. I need to make sure that x only takes json object with unique key. Below, example 'id' is the key, so i don't want to store other json objects with 'item1' key.
x = [{"id":"item1","val":"Items"},{"id":"item1","val":"Items"},{"id":"item1","val":"Items"}]
var clickId = // could be "item1", "item2"....
var found = $.inArray(clickId, x); //
if(found >=0)
{
x.splice(found,1);
}
else{
x.push(new Item(clickId, obj)); //push json object
}
would this accomplish what you're looking for? https://jsfiddle.net/gukv9arj/3/
x = [
{"id":"item1","val":"Items"},
{"id":"item1","val":"Items"},
{"id":"item2","val":"Items"}
];
var clickId = [];
var list = JSON.parse(x);
$.each(list, function(index, value){
if(clickId.indexOf(value.id) === -1){
clickId.push(value.id);
}
});
You can't use inArray() because you are searching for an object.
I'd recommend rewriting a custom find using Array.some() as follows.
var x = [{"id":"item1","val":"Items"},{"id":"item1","val":"Items"},{"id":"item1","val":"Items"}]
var clickId = "item1";
var found = x.some(function(value) {
return value.id === clickId;
});
alert(found);
Almost 6 years later i ended up in this question, but i needed to fill a bit more complex array, with objects. So i needed to add something like this.
var values = [
{value: "value1", selected: false},
{value: "value2", selected: false}
//there cannot be another object with value = "value1" within the collection.
]
So I was looking for the value data not to be repeated (in an object's array), rather than just the value in a string's array, as required in this question. This is not the first time i think in doing something like this in some JS code.
So i did the following:
let valueIndex = {};
let values = []
//I had the source data in some other and more complex array.
for (const index in assetsArray)
{
const element = assetsArray[index];
if (!valueIndex[element.value])
{
valueIndex[element.value] = true;
values.push({
value: element.value,
selected: false
});
}
}
I just use another object as an index, so the properties in an object will never be repated. This code is quite easy to read and surely is compatible with any browser. Maybe someone comes with something better. You are welcome to share!
Hopes this helps someone else.
JS objects are great tools to use for tracking unique items. If you start with an empty object, you can incrementally add keys/values. If the object already has a key for a given item, you can set it to some known value that is use used to indicate a non-unique item.
You could then loop over the object and push the unique items to an array.
var itemsObj = {};
var itemsList = [];
x = [{"id":"item1","val":"foo"},
{"id":"item2","val":"bar"},
{"id":"item1","val":"baz"},
{"id":"item1","val":"bez"}];
for (var i = 0; i < x.length; i++) {
var item = x[i];
if (itemsObj[item.id]) {
itemsObj[item.id] = "dupe";
}
else {
itemsObj[item.id] = item;
}
}
for (var myKey in itemsObj) {
if (itemsObj[myKey] !== "dupe") {
itemsList.push(itemsObj[myKey]);
}
}
console.log(itemsList);
See a working example here: https://jsbin.com/qucuso
If you want a list of items that contain only the first instance of an id, you can do this:
var itemsObj = {};
var itemsList = [];
x = [{"id":"item1","val":"foo"},
{"id":"item2","val":"bar"},
{"id":"item1","val":"baz"},
{"id":"item1","val":"bez"}];
for (var i = 0; i < x.length; i++) {
var item = x[i];
if (!itemsObj[item.id]) {
itemsObj[item.id] = item;
itemsList.push(item);
}
}
console.log(itemsList);
This is late but I did something like the following:
let MyArray = [];
MyArray._PushAndRejectDuplicate = function(el) {
if (this.indexOf(el) == -1) this.push(el)
else return;
}
MyArray._PushAndRejectDuplicate(1); // [1]
MyArray._PushAndRejectDuplicate(2); // [1,2]
MyArray._PushAndRejectDuplicate(1); // [1,2]
This is how I would do it in pure javascript.
var x = [{"id":"item1","val":"Items"},{"id":"item1","val":"Items"},{"id":"item1","val":"Items"}];
function unique(arr, comparator) {
var uniqueArr = [];
for (var i in arr) {
var found = false;
for (var j in uniqueArr) {
if (comparator instanceof Function) {
if (comparator.call(null, arr[i], uniqueArr[j])) {
found = true;
break;
}
} else {
if (arr[i] == uniqueArr[j]) {
found = true;
break;
}
}
}
if (!found) {
uniqueArr.push(arr[i]);
}
}
return uniqueArr;
};
u = unique(x, function(a,b){ return a.id == b.id; });
console.log(u);
y = [ 1,1,2,3,4,5,5,6,1];
console.log(unique(y));
Create a very readable solution with lodash.
x = _.unionBy(x, [new Item(clickId, obj)], 'id');
let x = [{id:item1,data:value},{id:item2,data:value},{id:item3,data:value}]
let newEle = {id:newItem,data:value}
let prev = x.filter(ele=>{if(ele.id!=new.id)return ele);
newArr = [...prev,newEle]
I am trying to achieve something which seemed very basic but is getting me mad over the last days.
I have a simple array : ["a","b","c","d","e"] and I want to turn it into a nested JSON like this:
{"a":{"b":{"c":{"d":{"e":""}}}}}
Looping over it, I ran in problems like "how do you save the last key to set it afterwards without erasing it" and so on.
Does anyone has an idea?
You might have had problems because you were looping in the wrong direction. Try to build the object from inside-out:
array.reduceRight(function(v, key) {
var o = {};
o[key] = v;
return o;
}, "")
or, with a loop:
var val = "";
for (var i=array.length; i--; )
var o = {};
o[array[i]] = val;
val = o;
}
return val;
Here's one way to do it, recursively:
function convertToNestedObject(arr) {
var result = {};
if (arr.length === 1) {
result[arr[0]] = '';
} else {
result[arr[0]] = convertToNestedObject(arr.slice(1, arr.length));
}
return result;
}
You could pass the start index in to the function instead of using slice and creating copies of the array:
function convertToNestedObject(arr, startIndex) {
var result = {};
if (arr.length - startIndex === 1) {
result[arr[startIndex]] = '';
} else {
result[arr[startIndex]] = convertToNestedObject(arr, startIndex + 1);
}
return result;
}
Example: http://jsfiddle.net/jwcxfaeb/1/
Put current element as key and empty object ({}) as value. Continue with newly inserted empty object.
function toNested(arr){
var nested = {};
var temp = nested;
for(var i=0; i<arr.length; i++){
temp[arr[i]] = {};
temp = temp[arr[i]];
}
return nested;
}
I am trying to create a javascript object like
var allUserExpiry={};
allUserExpiry[aData.userId][aData.courseId][aData.uscId] = aData;
But I am getting an error like allUserExpiry[aData.userId] undefined.
Is there a way, whereby I can set multi-level JS-Object keys? or is it important that I should go by doing allUserExpiry[aData.userId]={}, then allUserExpiry[aData.userId][aData.courseId]={} ?
Please let me know if there are any utility functions available for the same.
No, there is no way to set "multilevel keys". You need to initialize each object before trying to add properties to it.
var allUserExpiry = {};
allUserExpiry[aData.userId] = {}
allUserExpiry[aData.userId][aData.courseId] = {}
allUserExpiry[aData.userId][aData.courseId][aData.uscId] = aData;
Using Computed property names from ES6, it is possible to do:
var allUserExpiry = {
[aData.userId] = {
[aData.courseId]: {
[aData.uscId]: aData
}
}
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names
Simply use loadash,
let object = {};
let property = "a.b.c";
let value = 1;
_.set(object, property, value); // sets property based on path
let value = _.get(object, property, default); // gets property based on path
Or you can do it:
function setByPath(obj, path, value) {
var parts = path.split('.');
var o = obj;
if (parts.length > 1) {
for (var i = 0; i < parts.length - 1; i++) {
if (!o[parts[i]])
o[parts[i]] = {};
o = o[parts[i]];
}
}
o[parts[parts.length - 1]] = value;
}
And use:
setByPath(obj, 'path.path2.path', someValue);
This approach has many weak places, but for fun... :)
Why not just do this?
var allUserExpiry={};
allUserExpiry[aData.userId] = {aData.courseId: {aData.uscId: aData}};
I have a pretty hacky but short way of doing it in IE9+ as well as real browsers.
Given var path = 'aaa.bbb.ccc.ddd.eee'; where path is what your intending to make into an object and var result = {}; will will create the object {aaa: {bbb: {ccc: {ddd: {eee: {}}}}}
result = {}
path.split('.').reduce(function(prev, e) {
var newObj = {};
prev[e] = newObj;
return newObj;
}, result);
will store the object in result.
How it works:
split('.') converts the input into ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
reduce(function (...) {...}, result) runs through the array created by split, and for each entry will pass along a returned value to the next one. In our case we pass the new object through after adding the new object to the old one. This creates a chain of objects. reduce returns the last object you return inside of it, so we have to defined result beforehand.
This relies on using references so it won't be immediately clear how it works if you're expecting your code to be maintained by anyone else and should probably be avoided to be honest, but it works at least.
You can also use the following to create the initial structure:
var x = function(obj, keys) {
if (!obj) return;
var i, t;
for (i = 0; i < keys.length; i++) {
if (!t) {
t = obj[keys[i]] = {};
} else {
t[keys[i]] = {};
t = t[keys[i]];
}
}
};
var a = {};
x(a, ['A', 'B', 'C', 'D', 'E', 'F']);
Another approach without strings or array as argument.
function fillObject() {
var o = arguments[0];
for(var i = 1; i < arguments.length-1; i++) {
if(!o.hasOwnProperty(arguments[i])) {
o[arguments[i]] = {};
}
if(i < arguments.length-2) {
o = o[arguments[i]];
}else {
o[arguments[i]] = arguments[i+1]
}
}
}
var myObj = {"foo":{}};
fillObject(myObj,"back","to","the","future",2);
console.log(JSON.stringify(myObj));
// {"foo":{},"back":{"to":{"the":{"future":2}}}}
But I wouldn't use it :-) It's just for fun.
Because I don't like too much intelligent algorithm. (If it was in this category)
Using lodash you can do this easily (node exists and empty check for that node)..
var lodash = require('lodash-contrib');
function invalidateRequest(obj, param) {
var valid = true;
param.forEach(function(val) {
if(!lodash.hasPath(obj, val)) {
valid = false;
} else {
if(lodash.getPath(obj, val) == null || lodash.getPath(obj, val) == undefined || lodash.getPath(obj, val) == '') {
valid = false;
}
}
});
return valid;
}
Usage:
leaveDetails = {
"startDay": 1414998000000,
"endDay": 1415084400000,
"test": { "test1" : 1234 }
};
var validate;
validate = invalidateRequest(leaveDetails, ['startDay', 'endDay', 'test.test1']);
it will return boolean.
Another solution using reduce function (thanks Brian K).
Here we created a get/set to general proposes. The first function return the value in any level. The key is splited considering the separator. the function return the value refered from last index in the key's array
The second function will set the new value considering the last index of the splited key
the code:
function getObjectMultiLevelValue(_array,key,separator){
key = key.split(separator || '.');
var _value = JSON.parse(JSON.stringify(_array));
for(var ki in key){
_value = _value[key[ki]];
}
return _value;
}
function setObjectMultiLevelValue(_array,key,value,forcemode,separator){
key.split(separator || '.').reduce(function(prev, currKey, currIndex,keysArr) {
var newObj = {};
if(prev[currKey] && !forcemode){
newObj = prev[currKey];
}
if(keysArr[keysArr.length-1] == currKey){
newObj = value;
prev[currKey] = newObj;
}
prev[currKey] = newObj;
return newObj;
}, _array);
return _array;
}
//testing the function
//creating an array
var _someArray = {a:'a',b:'b',c:{c1:'c1',c2:{c21:'nothing here...'}}};
//a multilevel key to test
var _key = 'a,a1,a21';
//any value
var _value = 'new foo in a21 key forcing replace old path';
//here the new value will be inserted even if the path exists (forcemode=true). Using comma separator
setObjectMultiLevelValue(_someArray,_key,_value,true,',');
console.log('_someArray:');
console.log(JSON.stringify(_someArray));
//inserting another value in another key... using default separator
_key = 'c.c2.c21';
_value = 'new foo in c21 key';
setObjectMultiLevelValue(_someArray,_key,_value);
console.log('_someArray:');
console.log(JSON.stringify(_someArray));
//recovering the saved value with different separators
_key = 'a,a1,a21';
console.log(getObjectMultiLevelValue(_someArray,_key,','));
_key = 'c.c2.c21';
console.log(getObjectMultiLevelValue(_someArray,_key));
Let assume our object is
const data = {
//some other data
userInfo: {},
};
First, define a new property of that object
data.userInfo.vehicle = {};
then simply
data.userInfo.vehicle.vehicleType = state.userInfo.vehicleType;
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Dynamic object property name
I want to dynamically generate access to an object's property.
If I try to access mydata[i].val.name I get somename.
If I try it like mydata[i] + bar[j] (where bar[j] === '.val.name') it fails.
How do I dynamically create something like this? So that I can access any property of an object using a user generated value?
Some code:
If I have an object I want to be able to iterate through its properties, gathering the ones I am interested in. Ideally I would like something like the following:
var processData = function (data, keys, values) {
var returnData = [], i, j, k;
var parsedData = JSON.parse(data);
var keys = keys || null;
var values = values || null;
var datalen = parsedData.length;
for (i = 0; i < datalen; i++) {
returnData[i] = {};
for(j = 0; j< keys.length; j++){
for(k = 0; k < values.length; k++){
returnData[i][keys[j]] = parsedData[i] + values;
}
}
}
return returnData;
};
and then use it like:
var keys = ["foo","bar"];
var values = [".val.name", ".val.date"];
processData(data, keys, values);
But this does not work and in console I see foo="[object Object].val.name" rather than the expected foo="ACME Industries".
If you want to stick to your pattern of constructing the subscript as a string with dots in it you have to roll your own lookup function, like so:
function descend(object, sub) {
var handle = object,
stack = sub.split('.'),
history = [],
peek;
while (handle[stack[0]]) {
if (peek) {
history.push(peek);
}
peek = stack.shift();
handle = handle[peek];
}
if (stack.length > 0) {
history.push(peek);
throw "Traversal error, could not descend to '" + stack.join('.') + "' from '" + history.join('.') + "'.";
}
return handle;
}
var x = {
a: {
b: {
c: 15
},
d: 4
}
};
console.log(descend(x, "a"));
console.log(descend(x, "a.b"));
console.log(descend(x, "a.b.c"));
console.log(descend(x, "a.d"));
function processData(data, keys, values) {
if (keys.length !== values.length) {
throw "Mismatched keys and value lookups";
}
var i,
len = keys.length,
gathered = {},
k,
scratch,
v;
for (i = 0; i < len; i += 1) {
k = descend(data, keys[i]);
scratch = values[i].split('.');
scratch.shift();
v = descend(k, scratch.join('.'));
gathered[keys[i]] = v;
}
return gathered;
}
var data = {
foo: {
val: {
name: "ACME Industries"
}
},
bar: {
val: {
date: (new Date())
}
}
};
var keys = ["foo","bar"];
var values = [".val.name", ".val.date"];
processData(data, keys, values);
Please note: this will not be nearly as performant as coding without this style of lookup.
If you try:
new Object() + '.john.doe'
It will concatenate as a string, so you’ll get "[object Object].john.doe".
You should create a function that can handle dynamic property names instead (and there are plenty of those). You also might want to loose the ".foo.bar" syntax as a string (unless you plan to use eval()) and work solely with arrays instead.
If I understand correctly you need to use
mydata[i]["val"]["name"]
So, I'd use something like this:
var result =getItemByValuesPath(myData[i],values);
alert(result);
function getItemByValuesPath(item, values)
{
var result = item;
var vals = values.split(".");
for(var j=0; j<values.length; j++)
{
if(result==undefined)
{
return null;
}
result = result[values[j]];
}
}