Get array from variable in javascript - javascript

I'm looking to get an array from a variable. If it's not already an array I want to return a new array with the variable as the only entry.Example:
toArray('test'); // => ["test"]
toArray(['test']); // => ["test"]
My actual working code is:
var toArray;
toArray = function(o) {
if (Array.isArray(o)) {
return o.slice();
} else {
return [o];
}
};
I'd like to know if there is a nicer way for that (native or with underscore.js for example).
In ruby, you can do:
Array('test') # => ["test"]
Array(['test']) # => ["test"]

Just use .concat().
[].concat("test"); // ["test"]
[].concat(["test"]); // ["test"]
Arrays will be flattened into the new Array. Anything else will simply be added.

function toArray(o) {
return Array.isArray(o) ? o.slice() : [o];
};

I believe you can slice it:
var fooObj = {0: 123, 1: 'bar', length: 2};
var fooArr = Array.prototype.slice.call(fooObj);
Demo: http://jsfiddle.net/sh5R9/

Related

Recursive split while delimiter exists in the string and then create an object

Suppose I have the following string: plan.details.notes (Note: this string can have more/less sub nodes) and I have a value that should be set to it once it has been destructured, ie. "hello".
Here's what I want to achieve:
{
plan {
details {
notes: "hello"
}
}
}
I currently have the following code:
function recursiveSplitIntoObject(string, delimiter, value) {
if (!string) return {}
let stringToSplit = string
const returnObj = {}
while (stringToSplit.indexOf(delimiter) >= 0) {
const split = string.split(delimiter)
returnObj[split[0]] = { [split[1]]: value }
stringToSplit = split[1]
}
console.log(returnObj)
}
I'm not sure how to assign a dynamic object inside the [split[1]]: value. I maybe close but I can't figure it out. Can anyone lead me to the right direction? Thanks!
Remove the last key, process all other keys and finally assign the last key:
function recursiveSplitIntoObject(string, delimiter, value) {
let start = {},
curr = start,
keys = string.split(delimiter),
last = keys.pop()
for (let key of keys)
curr = (curr[key] = {})
curr[last] = value
return start
}
console.log(
recursiveSplitIntoObject('plan.details.notes', '.', 'hello'))
In that case I would really consider using recursion, as it simplifies the way we can think of this problem.
The idea is to split the string into chunks using the delimeter, and when reaching to the last piece, assign the value, otherwise, call the recursive function with the tail of the array:
function rec(chunks, value) {
if (chunks.length === 1) {
return { [chunks[0]]: value };
}
return { [chunks[0]]: rec(chunks.slice(1), value) };
}
function recrusiveSetValue(str, value) {
const chunks = str.split('.');
return rec(chunks, value);
}
console.log(recrusiveSetValue('foo.bar.baz', 'hello'))
reduceRight makes this a one-liner:
const recursiveSplitIntoObject = (string, delimiter, value) =>
string .split (delimiter) .reduceRight ((v, n) => ({[n]: v}), value)
console .log (recursiveSplitIntoObject ('plan.details.notes', '.', 'hello'))
We simply build this from the inside out, starting with 'hello', then {notes: 'hello'}, then {details: {notes: 'hello'}} and finally {plan: {details: {notes: 'hello'}}}.
You could also look at more generic tools, used in the case where you want to create a single object from multiple pairs such as ['plan.details.notes', 'hello']. I keep handy a function like setPath that would let you add your path into an existing object:
const setPath = ([p, ...ps]) => (v) => (o) =>
p == undefined ? v : Object .assign (
Array .isArray (o) || Number .isInteger (p) ? [] : {},
{...o, [p]: setPath (ps) (v) ((o || {}) [p])}
)
const recursiveSplitIntoObject = (string, delimiter, value, original) =>
setPath (string .split (delimiter)) (value) (original)
console .log (
recursiveSplitIntoObject (
'plan.details.notes',
'.',
'hello',
{foo: 'bar', plan: {summary: 'world'}}
)
)
// {
// foo: "bar",
// plan: {
// summary: "world",
// detail": {
// notes: "hello"
// }
// }
// }
This may be overkill here, but it could be good to keep in mind.

How to convert Javascript Array to array of functions?

I had a question in my interview where they asked me to transform an array to array of functions like below:
var a = ["a", 24, { foo: "bar" }];
var b = transform(a);
console.log(a[1]); // 24
console.log(b[1]()); // 24
I tried many combinations but I couldn't really do what they ask. Can someone tell me how to implement the transform function?
I'm not exactly sure about their intention, but a simple approach to provide the given results would be as follows:
function transform(a){
return a.map( (el) => () => el );
}
PS: the interesting follow up question would then be about the differences between the results for b[0]() and b[1]() compared to b[2]() and how to mitigate the problems that might arise for the later.
The desired behavior is a bit weird but you can do as follow:
const transform = arr => arr.map(elmt => () => elmt)
You can use a nice feature here, Proxy...
var arr = ["a", 24, { foo: "bar" }];
function transform(arr){
return new Proxy(arr, {
get: (obj, prop) => { return ()=>obj[prop] }
});
}
var newArr = transform(arr);
newArr[0]();
One approach you could take is to call Array.prototype.map method on your original array a - the map method will return a new array. Each index of the new array will have get a value of an anonymous function definition that, when called, will return the value of the corresponding index in the original array.
var a = ["a", 24, {
foo: "bar"
}];
var b = transform(a);
console.log(a[1]); // 24
console.log(b[1]()); // 24
function transform(array) {
return array.map(function(el) {
return function() {
return el;
}
});
}

JavaScript - Filter <key,value> Object by key

I am looking for a short and efficient way to filter objects by key, I have this kind of data-structure:
{"Key1":[obj1,obj2,obj3], "Key2":[obj4,obj5,obj6]}
Now I want to filter by keys, for example by "Key1":
{"Key1":[obj1,obj2,obj3]}
var object = {"Key1":[1,2,3], "Key2":[4,5,6]};
var key1 = object["Key1"];
console.log(key1);
you can use the .filter js function for filter values inside an object
var keys = {"Key1":[obj1,obj2,obj3], "Key2":[obj4,obj5,obj6]};
var objectToFind;
var keyToSearch = keys.filter(function(objects) {
return objects === objectToFind
});
The keyToSearch is an array with all the objects filter by the objectToFind variable.
Remember, in the line return objects === objectToFind is where you have to should your statement. I hope it can help you.
You can create a new object based on some custom filter criteria by using a combination of Object.keys and the array .reduce method. Note this only works in es6:
var myObject = {"Key1":["a","b","c"], "Key2":["e","f","g"]}
function filterObjectByKey(obj, filterFunc) {
return Object.keys(obj).reduce((newObj, key) => {
if (filterFunc(key)) {
newObj[key] = obj[key];
}
return newObj;
}, {});
}
const filteredObj = filterObjectByKey(myObject, x => x === "Key1")
console.log(filteredObj)
Not sure what exactly are you trying to achieve, but if you want to have a set of keys that you would like to get the data for, you have quite a few options, one is:
var keys = ['alpha', 'bravo'];
var objectToFilterOn = {
alpha: 'a',
bravo: 'b',
charlie: 'c'
};
keys.forEach(function(key) {
console.log(objectToFilterOn[key]);
});

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"}`

Round off decimals values in all objects of a Json object

This is my JSON string
[{"$id":"1","GeoName":"EAST","Value2":87.88221970554928,"Value4":90.71955219607294,"Value1":18.44377295716579,"Value3":16.732108234801206},{"$id":"2","GeoName":"WEST","Value2":87.88221970554928,"Value4":90.71955219607294,"Value1":18.44377295716579,"Value3":16.732108234801206}]"
This is my JSONobject before Stringyfying
**MyObj:**
0:Object
$id:"1"
Value1:18.44377295716579
GeoName:"EAST"
Value2:87.88221970554928
Value3:16.732108234801206
Value4:90.71955219607294
1:Object
$id:"2"
Value1:18.44377295716579
GeoName:"WEST"
Value2:87.88221970554928
Value3:16.732108234801206
Value4:90.71955219607294
Is there a way to round off all the values(or even individually value1,value2...) upto 2 decimal points in each every object of my JSON?
Like
0:Object
$id:"1"
Value1:18.44
GeoName:"EAST"
Value2:87.89
Value3:16.73
Value4:90.72
1:Object
$id:"2"
Value1:18.44
GeoName:"WEST"
Value2:87.89
Value3:16.73
Value4:90.71
I have tried using
Obj = JSON.parse(JSON.stringify(MyObj, function (GeoName, Value1) {
return Value1.toFixed ? Number(Value1.toFixed(2)) : Value1;
}));
Which did not work for me.
Is there a way i can achieve this by tweaking the above line of code or is there a better approach to solve this issue?
Edit1(After the approach suggested by wostex)
var numKeys = ['ShareValue','ReachValue','DepthValue','WidthValue'];
MyObj=Obj.map(e => Object.assign(e,Object.keys(e).filter(k => numKeys.includes(k)).map(v => Object.assign({[v]: parseFloat(e[v]).toFixed(2)}))))
This is How my object is looking like
0:Object
0:Object
ReachValue:87.88
1:Object
DepthValue:16.73
2:Object
ShareValue:18.44
3:Object
WidthValue:90.71
$id:"1"
ShareValue:18.44377295716579
GeoName:"EAST"
ReachValue:87.88221970554928
DepthValue:16.732108234801206
WidthValue:90.71955219607294
Edit2
Obj = JSON.parse(JSON.stringify(BubbleObj));
var numKeys = ['ShareValue','ReachValue','DepthValue','WidthValue'];
var MyObj=Obj.map(e => Object.assign(e,Object.keys(e).filter(k => numKeys.includes(k)).map(v => Object.assign({[v]: parseFloat(e[v]).toFixed(2)}))))
Here Obj has the JSON string from the first line of the question and BubbleObj has the Object which i mentioned as MyObj in the question.
The easy way is just to use map - and as long as your Value1-4 are always called that
var input = [{"$id":"1","GeoName":"EAST","Value2":87.88221970554928,"Value4":90.71955219607294,"Value1":18.44377295716579,"Value3":16.732108234801206},{"$id":"2","GeoName":"WEST","Value2":87.88221970554928,"Value4":90.71955219607294,"Value1":18.44377295716579,"Value3":16.732108234801206}];
var output = input.map(function(e) {
return {
$id: e.$id,
GeoName: e.GeoName,
Value1: Math.round(e.Value1*100) /100,
Value2: Math.round(e.Value2*100) /100,
Value3: Math.round(e.Value3*100) /100,
Value4: Math.round(e.Value4*100) /100
}
});
console.log(output);
If you dont want to hardcode it, you could look for any key starting Value - its a little more complex.
var input = [{"$id":"1","GeoName":"EAST","Value2":87.88221970554928,"Value4":90.71955219607294,"Value1":18.44377295716579,"Value3":16.732108234801206},{"$id":"2","GeoName":"WEST","Value2":87.88221970554928,"Value4":90.71955219607294,"Value1":18.44377295716579,"Value3":16.732108234801206}];
var output = input.map(function(e) {
return Object.keys(e).reduce(function(p,n){
if(n.startsWith("Value"))
p[n] = Math.round(e[n]*100)/100;
else
p[n] = e[n];
return p;
},{})
});
console.log(output);
BTW you have duplicated keys 'Value3' in your first string, other than that you can do the following:
var j = [{"$id":"1","GeoName":"EAST","ShareValue":87.88221970554928,"ReachValue":90.71955219607294,"DepthValue":18.44377295716579,"WidthValue":16.732108234801206},{"$id":"2","GeoName":"WEST","ShareValue":87.88221970554928,"ReachValue":90.71955219607294,"DepthValue":18.44377295716579,"WidthValue":16.732108234801206}];
// your numeric keys
var numKeys = ['ShareValue','ReachValue','DepthValue','WidthValue'];
var res = j.map(e => Object.assign(e, // rewrite each object
...Object.keys(e).filter(k => numKeys.indexOf(k) > -1) // pick keys which include 'Value'
.map(v => Object.assign({[v]: parseFloat(e[v]).toFixed(2)})) // chop numbers
))
console.log(res)

Categories