abstract javascript code into ES6 function - javascript

I have some code that is working but I want to abstract it into a functional component I can use elsewhere in my script. I am getting an undefined error:
This works:
//add an index to each element
var items = learning_check.map(function(el,i) {
var o = Object.assign({}, el);
o.key = i;
return o;
});
this doesn't:
const addIndex = (a) => {
console.log('addIndex initiated')
a.map = (el,i) => {
var o = Object.assign({}, el);
o.key = i;
return o;
}
}
called with
const mItems = addIndex(learning_check); // initiated
console.log('mItems: ' + mItems); // undefined

Firstly, I'd like to say that you're on the right track. You're missing two things.
You need to call map and not reassign it, like a.map(...) and not a.map = .... And, you need to return the result of map from your addIndex function. Like so,
const addIndex = (a) => {
console.log('addIndex initiated');
// Notice the return
return a.map((el, i) => { // See how we call map here
var o = Object.assign({}, el);
o.key = i;
return o;
});
}
// Mock
const learning_check = [{
id: "abcde"
}, {
id: "fghij"
}];
const mItems = addIndex(learning_check); // initiated
console.log('mItems: ' + JSON.stringify(mItems));
I'll suggest a simplification to your code here, you could use it if you like
const addIndex = (a) => {
console.log('addIndex initiated')
return a.map((el, i) => Object.assign({
key: i
}, el));
}
// Mock
const learning_check = [{
id: "abcde"
}, {
id: "fghij"
}];
const mItems = addIndex(learning_check); // initiated
console.log('mItems: ' + JSON.stringify(mItems));

Two bugs in your code
1) You are not returning anything from the function.
2) You are not calling the map function.
const addIndex = (a) => {
console.log('addIndex initiated')
return a.map((el, i) => {
var o = Object.assign({}, el);
o.key = i;
return o;
})
}

Related

Looping data from json using Array

I'm trying to write a function but I doesn't make it. This function works like that
Input: changeSetting("a>b>c","hello")
After that "setting" named value change from {} to {"a":{"b":{"c":"hello"}}}
If input is changeSetting("a","hello") json become {} to {"a":"hello"}
My last code attempt:
function changeSetting(name,val) {
if (name.includes(">")) {
name = name.split('>')
let json = {}
name.map((el,i)=>{
let last = ""
name.filter(el=>!name.slice(i+1).includes(el)).map(el=> {
if(last!="") {
json[el] = {}
}})
})
}
}
How can we make this ? (Optimization not important but if is it good for me)
const changeSetting = (setting, target) => {
if (setting.length < 2) {
return {
[setting]: target
}
} else {
const keys = setting.split('>');
return keys.reduceRight((acc, curr, i) => {
console.log(acc);
if(i === keys.length - 1) {
return acc = {[curr] : target}
}
return acc = { [curr]: acc };
}, {})
}
}
console.log(changeSetting('a', 'hello'));
console.log(changeSetting('a>b>c', 'hello'));
function changeSetting(inputProperties, value) {
let result;
const properties = inputProperties.split(">");
result = `{${properties
.map((property) => `"${property}":`)
.join("{")}"${value}"${"}".repeat(properties.length)}`;
return result;
}
changeSetting("a>b>c", "hello");
changeSetting("a", "hello");
As you work with strings - you may try to use JSON like this:
function changeSetting(name, val) {
const keys = name.split(">");
return JSON.parse(
[
"{",
keys.map((key) => `"${key}"`).join(":{"),
":",
`"${val}"`,
"}".repeat(keys.length),
].join("")
);
}
There's multiple ways to do this, I've commented the snippet
const changeSetting = (name, val) => {
// Split and reverse the name letters
const nameSplit = name.split('>').reverse();
// Set up the inner most object
let newObj = {[nameSplit[0]]:val}
// Now remove the first letter and recurse through the rest
nameSplit.slice(1).forEach((el, idx) => newObj = {[el]: newObj});
console.log(newObj);
}
changeSetting("a>b>c", "hello")
changeSetting("a", "hello")
changeSetting("a>b>c>d>e>f>g", "hello")
You can create an array by splitting name on all > with String.prototype.split(), and then Array.prototype.reduceRight() the created array of elements with an object initial value {} and adding key value pairs but on the last element the value should be variable val.
Code:
const changeSetting = (name, val) => name
.split('>')
.reduceRight((a, c, i, arr) => ({
[c]: i === arr.length - 1 ? val : a
}), {})
console.log(changeSetting('a>b>c', 'hello'))
console.log(changeSetting('a', 'hello'))
console.log(changeSetting('a>b>c>d>e>f>g', 'hello'))

typescript : logic to covert string array into custom object

Here is my requirement. I was able to achieve to some level in java but we need to move it to typescript (client side).
Note: The below input is for example purpose and may vary dynamically.
Input
var input = ["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"];
We need to create an utility function that takes above input and returns output as below.
Output:
Should be string not an object or anything else.
"{ a { name, type }, b { city {name, zip } , desc }, c }"
any help is much appreciated.
I don't see that typescript plays any role in your question, but here's a solution for constructing the string you requested. I first turn the array into an object with those properties, then have a function which can turn an object into a string formatted like you have
const input = ["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"];
const arrayToObject = (arr) => {
return arr.reduce((result, val) => {
const path = val.split('.');
let obj = result;
path.forEach(key => {
obj[key] = obj[key] || {};
obj = obj[key];
});
return result;
}, {});
}
const objectToString = (obj, name = '') => {
const keys = Object.keys(obj);
if (keys.length === 0) {
return name;
}
return `${name} { ${keys.map(k => objectToString(obj[k], k)).join(', ')} }`;
}
const arrayToString = arr => objectToString(arrayToObject(arr));
console.log(arrayToString(input));
Here's another variation. Trick is to parse the strings recursively and store the intermediate results in an Object.
function dotStringToObject(remainder, parent) {
if (remainder.indexOf('.') === -1) {
return parent[remainder] = true
} else {
var subs = remainder.split('.');
dotStringToObject(subs.slice(1).join('.'), (parent[subs[0]] || (parent[subs[0]] = {})))
}
}
var output = {};
["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"].forEach(function(entry) {
dotStringToObject(entry, output)
});
var res = JSON.stringify(output).replace(/\"/gi, ' ').replace(/\:|true/gi, '').replace(/\s,\s/gi, ', ');
console.log(res)
// Prints: { a { name, type }, b { city { name, zip }, desc }, c }
You could do something like this:
var input = ["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"];
var output = {};
for(var i =0; i < input.length; i+=2){
output[String.fromCharCode(i+97)] = {};
output[String.fromCharCode(i+97)].name = input[i];
output[String.fromCharCode(i+97)].type = input[i+1];
}
console.log(JSON.stringify(output));

Transform elements of an array into objects / Javascript

I have a challenge to create a simple Notes manager in JS, I've written a function that takes one string, gives it and id and pushes it to an array of notes.
let nextId = 0;
const getId = () => nextId++;
let notes = [{id: getId(), value: 'Note'}];
const addNote = (input) => {
notes.push({id:getId(), value: input});
console.log('Note added');
I now struggle with a function that will take multiple strings as parameters
('own', 'snail', 'platypus')
create an object for each element with id/value(string) and push it to the main array.
The result should look like:
[{ id: 1, value: 'owl'},
{ id: 2, value: 'snail'}]
So far I have this, it assigns ID correctly, but the loop fails
const batchAddNotes = (values) => {
let obj = {};
for (i = 0; i < values.length; i++) {
obj.id = (getId());
obj.value = (values[i]);}
return obj;};
To have your variables in a certain scope, I'd pack it all in a class (or a function). As you're using arrow functions, the class should be ok. To add multiple nodes the way you've shown; using var-args, you can create a method that expects those with (...input)
class Notes {
constructor() {
this.nextId = 0;
this.nodes = [{
id: this.getId(),
value: 'Note'
}];
}
addNote(input) {
this.nodes.push({
id: this.getId(),
value: input
})
}
getId() {
return this.nextId++;
}
addNotes(...input) {
input.forEach(e => this.addNote(e));
}
}
const notes = new Notes();
notes.addNotes('own', 'snail', 'platypus');
console.log(notes.nodes);
Use the functions arguments object. It's an array of all the arguments that are being passed to a function. Then you can loop over them and run your functionality on them each time.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
You could use the arguments passed in your function as var args
const addNote = _ => {
for(var i = 0; i < arguments.length; i++){
notes.push({id:getId(), value: arguments[i]});
console.log('Note added');
}
}
use rest params :
const myFn = (...values) => {
let tmpArr = [];
for(let i = 0 ; i < values.length ; i++){
tmpArr.push({
id : i + 1,
value : values[i]
});
}
return tmpArr;
}
const result = myFn('own', 'snail', 'platypus');
console.log(result);
This is how it look like when using Rest Params and reusing your first function. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
You can add so many notes as you want (addMultipleNotes can receive indefinite number of arguments )
let nextId = 0;
const getId = () => nextId++;
let notes = [{id: getId(), value: 'Note'}];
const addSingleNote = (input) => {
notes.push({id:getId(), value: input});
console.log('Note added');
};
const addMultipleNotes = (...args) => {
for(let i = 0; i < args.length; i++){
addSingleNote(args[i]);
}
};
addMultipleNotes('one', 'two', 'three');
console.log(notes);
First of all, note how I've used an IIFE and a closure to create an id generator.
In the other hand, rest parameters, Array#map and parameter spread are your friends:
const incrId = (() => {
let id = 0
return () => ++id
})()
const valuesToNotes = (...values) => values.map(value => ({
id: incrId(),
value
}))
const notes = []
// Parameter spread (i.e. '...') gives each
// array item in the output of valuesToNotes
// as if you would use Function#apply
notes.push(...valuesToNotes('a', 'b', 'c'))
console.log(notes)
Yet another more functional approach which doesn't mutate the input notes and produces a new one with existing notes plus the ones transformed from values:
const concat = xs => ys => xs.concat(ys)
const map = f => xs => xs.map(f)
const pipe = xs => x => xs.reduce((r, f) => f(r), x)
const incrId = (() => {
let id = 0
return () => ++id
})()
const valueToNote = value => ({
id: incrId(),
value
})
const notes = []
const appendNotes = pipe([map(valueToNote), concat(notes)])
const moreNotes = appendNotes(['a', 'b', 'c'])
console.log(moreNotes)

const is undefined within reduce method

avgPrice(data) {
const min_price = data.min_price,
max_price = data.max_price;
console.log(min_price) //has value
let x = _.reduce(data.custom_pricing, (a, b) => {
a.min_price = Math.min(a.min_price, b.min_price);
a.max_price = Math.max(a.max_price, b.max_price);
}, { min_price:min_price, max_price:max_price });
}
What's wrong with my code above? I got min_price is undefined error. But my const min_price has value when I do console.log.
You have to return from reduce (return a so it can be used for the next iteration).
let x = _.reduce(data.custom_pricing, (a, b) => {
a.min_price = Math.min(a.min_price, b.min_price);
a.max_price = Math.max(a.max_price, b.max_price);
return a; //<<<<<
}, { min_price:min_price, max_price:max_price });
No mutation:
Although I don't see a reason why.
let x = _.reduce(data.custom_pricing, (a, b) =>
({ min_price: Math.min(a.min_price, b.min_price), max_price: Math.max(a.max_price, b.max_price) }), // create a new object and return it
{ min_price:min_price, max_price:max_price });

How to write a function that inlines object properties

I have the following object:
var ob = {
view: {
name: 'zpo',
params: {
taskId: 3,
zuka: 'v'
}
}
}
I need to have this object in the following form:
{
"view.name":"zpo",
"view.params.taskId":3,
"view.params.zuka":"v"
}
I have written a function that can do that, but the problem is that it requires external variables passed to it. Here is this function:
function inline(o, result, container) {
for (var p in o) {
if (typeof o[p] === "object") {
inline(o[p], result.length > 0 ? result+'.'+p : p, container);
} else {
container[result + '.' + p] = o[p];
}
}
}
var ob = {
view: {
name: 'zpo',
params: {
taskId: 3,
zuka: 'v'
}
}
}
var c = {};
var r = inline(ob, '', c);
Is there any way to write this function to return correct result without the need to pass result and container external variables?
If i understood you correctly, you want to avoid to call your inline() function with "empty" params.
You could catch this case in your function directly:
function inline(o, result, container) {
result = result || '';
container = container || {};
...
}
var r = inline(ob);
you would still need this params for the recursive part of your function.
Here is a version that does not require any parameters.
// Return an array containing the [key, value] couples of an object.
const objEntries = o => Object.keys(o).map(k => [k, o[k]]);
// Create an object from an array of [key, value] couples.
const entriesObj = (entries, init={}) => entries.reduce((result, [key, val]) => {
result[key] = val;
return result;
}, init);
// Reduce on the object entries (as returned by objEntries) with an empty object as
// initialiser.
const inline = (o) => objEntries(o).reduce((result, [key, val]) => {
if(val instanceof Object){
// If the value is an object, recursively inline it.
const inlineVal = inline(val);
// Prefix each property names of inlineVal with the key of val and add the
// properties to the result object.
entriesObj(
objEntries(inlineVal).map(([subK, subVal]) => [key + '.' + subK, subVal]),
result
);
} else {
// If val is not an object, just add it to the result as is.
result[key] = val;
}
// Return the result.
return result;
}, {});
var ob = {
view: {
name: 'zpo',
params: {
taskId: 3,
zuka: 'v'
}
}
}
var r = inline(ob);
console.log(r);
I used arrow functions and destructuring. Old browsers won't support it. If you need to support them, just replace the arrow functions with regular functions and manually destructure the arguments.
javascript is awesome!
function inline(o, result, container) {
result = result || '';
container = container || {};
for (var p in o) {
if (typeof o[p] === "object") {
inline(o[p], result.length > 0 ? result+'.'+p : p, container);
} else {
container[result + '.' + p] = o[p];
}
}
}
var r = inline(ob);

Categories