Divide S3 prefix to list of object in Javascript - javascript

Example, I have a path(prefix) like this: A/B/C/
I want to get bellow list:
[{name:"A",path:"A/"},
{name:"B",path:"A/B/",
{name:"C",path:"A/B/C/"
]
I can split the path to a array, then loop the array to build the new list of object.
But in my mind, I just know there should be a simple and smarter way to achieve this by using reducer, but just stuck here.

You're right that you could use a reducer. Something like this:
const str = "A/B/C/"
const arr = str.split("/").filter(Boolean).reduce((acc, name) => {
const path = [...acc.map(o => o.name), name].join("/") + "/"
return [...acc, { name, path }]
}, [])
console.log(arr)

You can solve this with map, but maybe not as cleanly as you were anticipating:
const result = 'A/B/C'
.split('/')
.filter(x => x)
.map((name, i, arr) => {
const prev = arr[i - 1];
return prev
? { name, path: `${prev.name}${name}/` }
: { name, path: `${name}/` };
});

My odd solution, but I think sdgluck's is the cleanest answer.
arr = "A/B/C/"
.split("/")
.filter(e => e)
.map((e, i, a) => {
a2 = a.filter((el, ix) => {
if (ix <= i) return el;
});
return {[e] : a2.join("/")};
});

Related

Assign mutiple ranges (non consecutive) to a single Constants (apps script)

I have this (below) as part of my apps script and I am wondering if there is a way to simplify this:
const fieldRange = ["C9","C10","C11","C12","C15","C16","C17","C18","C19","C20","C21","C22","C23","C24","C25","C26","C27","C28","C29","C30","C33","C34","C35","C36","C37","C38","C41","C42","C45","C46","C47","C48","C49","C50"]
maybe something like ['(C9:C12)','(C15:C30)', etc.
In your situation, how about the following sample script?
Sample script:
// This value is from your question.
const fieldRange = ["C9", "C10", "C11", "C12", "C15", "C16", "C17", "C18", "C19", "C20", "C21", "C22", "C23", "C24", "C25", "C26", "C27", "C28", "C29", "C30", "C33", "C34", "C35", "C36", "C37", "C38", "C41", "C42", "C45", "C46", "C47", "C48", "C49", "C50"];
// Ref: https://tanaikech.github.io/2021/10/08/compiling-continuous-numbers-using-google-apps-script/
const compilingNumbers = (ar) => {
const { values } = [...new Set(ar.sort((a, b) => a - b))].reduce((o, e, i, a) => {
if (o.temp.length == 0 || (o.temp.length > 0 && e == o.temp[o.temp.length - 1] + 1)) {
o.temp.push(e);
} else {
if (o.temp.length > 0) {
o.values.push({ start: o.temp[0], end: o.temp[o.temp.length - 1] });
}
o.temp = [e];
}
if (i == a.length - 1) {
o.values.push(o.temp.length > 1 ? { start: o.temp[0], end: o.temp[o.temp.length - 1] } : { start: e, end: e });
}
return o;
}, { temp: [], values: [] });
return values;
};
const r1 = fieldRange.reduce((o, e) => {
const k = e.replace(/[0-9]+/, "");
const v = Number(e.toUpperCase().replace(/[A-Z]+/, ""));
o[k] = o[k] ? [...o[k], v] : [v];
return o;
}, {});
const res = Object.entries(r1).flatMap(([k, v]) => compilingNumbers(v).map(({ start, end }) => `${k}${start}:${k}${end}`));
console.log(res); // ["C9:C12","C15:C30","C33:C38","C41:C42","C45:C50"]
When this script is run, ["C9:C12","C15:C30","C33:C38","C41:C42","C45:C50"] is obtained.
If you want to retrieve the value like maybe something like ['(C9:C12)','(C15:C30)', etc., please modify ${k}${start}:${k}${end} to (${k}${start}:${k}${end}).
Note:
From your showing sample value, this script supposes that all values in your expected array including A1Notation are only one cell. Please be careful about this.
References:
reduce()
map()

using reduce to compose an array of cumulative strings

I want to return something like this ['4', '42','420'] for a given number: 420
I am using reduce to accumulate the number and using the index to concat the number in the index before. First two iterations are working fine, but in the third one I am having trouble thinking how to deal with the result since it is undefined => [ '4', '42', '4,42undefined' ]
Any thoughts?
function createArrayOfTiers(num) {
const onArray = String(num).split('')
onArray.reduce((acc, curr, index) => {
if (!index) return curr
const newAcc = [...acc, acc + curr[index -1]]
return newAcc
},[])
}
createArrayOfTiers(420)
If you want to collect the incremental digits of a number, I think a clearer approach would be to use .map (or Array.from) instead: on each iteration, slice the (stringified) number from index 0 to the current index.
function createArrayOfTiers(num) {
const strNum = String(num);
return [...strNum].map((_, i) => strNum.slice(0, i + 1));
}
console.log(createArrayOfTiers(420));
To fix your existing code, you'll want to access the previous element of the accumulator, not the previous element of curr (which doesn't really make sense), multiply it by 10, and add to it the digit being iterated over. Also make sure to return the constructed array:
function createArrayOfTiers(num) {
const onArray = String(num).split('')
return onArray.reduce((acc, curr, index) => {
if (!index) return Number(curr);
return [...acc, acc[index - 1] * 10 + Number(curr)];
}, [])
}
console.log(createArrayOfTiers(420));
You have the error because you forgot to take first number in the third iteration. You take acc + curr[index - 1] but forgot about acc + curr[index - 2].
To fix it you can slice numbers from first to current and then join them.
function createArrayOfTiers(num) {
const onArray = String(num).split("");
return onArray.reduce((acc, curr, index, arr) => {
const newAcc = [...acc, arr.slice(0, index + 1).join("")];
return newAcc;
}, []);
}
console.log(createArrayOfTiers(420));
Try this:
function createArrayOfTiers(num) {
const str = String(num);
const res = str.split('').reduce((acc, curr, index) => {
acc = [...acc, str.substring(0,index)+curr];
return acc;
}, []);
return res;
}
console.log( createArrayOfTiers(420) );
You could map the stringed value by using a closure of a string which stores the value of the last result.
function createArrayOfTiers(num) {
return Array.from(num.toString(), (s => v => s += +v)(''));
}
console.log(createArrayOfTiers(420));
The reduce aspect ...
function createArrayOfTiers(num) {
return [...num.toString()].reduce((r, v) => [...r, (r[r.length - 1] || '') + v], []);
}
console.log(createArrayOfTiers(420));
my way
const createArrayOfTiers = arg =>
[...String(arg)].reduce((r,c,i)=>[...r, (r[i-1]||'')+c],[])
console.log('420 -->' ,JSON.stringify( createArrayOfTiers(420) ));
console.log("'elisa' -->" ,JSON.stringify( createArrayOfTiers('elisa') ));
.as-console-wrapper{max-height:100% !important;top: 0;}
thanks to CertainPerformance in this thread I understood that I was trying to access the index of an inexistent array in curr therefore I needed to access the index of the acc value and came up to this solution using an updated version of my code:
function createArrayOfTiers(num) {
const onArray = String(num).split('')
const reducer = onArray.reduce((acc, curr, index) => {
if (!index) return [curr]
const newAcc = [...acc, acc[index -1] + curr]
return newAcc
},[])
return reducer
}
createArrayOfTiers(420)
I also transformed curr to an array because in case of only one digit, should return an array as well.
Thanks to all who gave brilliant ideas!!

Javascript - Delete all duplicates from array

I have problem with delete all duplicate in array.
Array = [1,1,2,2,3]
Every solution, what I found, haves result this
Array = [1,2,3]
But I need this
Array = [3]
How can I do this?
You can first iterate over the array once to obtain a Map of the frequencies of each item and then filter to find the elements that only appeared once.
const arr = [1,1,2,2,3];
const freq = arr.reduce((acc,curr)=>(acc.set(curr,(acc.get(curr)||0)+1),acc),new Map);
const res = arr.filter(x => freq.get(x) === 1);
console.log(res);
You could store an object for occurences of each element and get the elements that have the occurence of 1
const arr = [1, 1, 2, 2, 3]
const occurrences = arr.reduce((acc, el) => {
acc[el] = (acc[el] || 0) + 1
return acc
}, {})
const res = Object.entries(occurrences)
.filter(([el, time]) => time === 1)
.map(([el]) => +el)
console.log(res)
Unlike some of the other solutions, this allows you to make a single loop over the array, rather than a reduce followed by a filter and/or map loop. That said, there are trade-offs in readability and other condition checks, and it plays a bit fast and loose with the semantic intention of a reduce, so it might be a wash in terms of benefits.
const myArray = [1,1,2,2,3];
const dupesRemoved = myArray.reduce((acc, cur, idx, src) => {
if (!acc.dupes.has(cur)) {
if (acc.singleInstances.has(cur)) {
acc.singleInstances.delete(cur);
acc.dupes.add(cur);
} else {
acc.singleInstances.add(cur);
}
}
if (idx === src.length - 1) {
return [...acc.singleInstances];
}
return acc;
}, { singleInstances: new Set(), dupes: new Set() });
console.log(dupesRemoved);
Here is a simple and short solution:
let arr = [1,1,2,2,3];
let filtered_arr = arr.filter(v => arr.indexOf(v) === arr.lastIndexOf(v));
console.log(filtered_arr);

List traversal with two adjacent elements in a Functional way

I'm trying to implement functional version of the below code
const adjacent = (list) => {
let results = [];
for (let idx = 0; idx < list.length - 1; idx++) {
const computedRes = someComplexFn(list[idx], list[idx + 1]);
results.push(computedRes );
}
return results;
}
i have come with the following version
const locations = [1,2,3,4,5];
const calcRatioFn = (x, y) => x+y;
const adjacentMap = (list, result=[]) => {
if(R.length(list) < 2) {
return result;
}
const f1 = R.head(list);
const f2 = R.tail(list);
result.push(calcRatioFn(f1 ,R.head(f2)));
return adjacentMap(R.tail(list), result);
}
const results = adjacentMap(locations);
console.log(results);
Are any any other simple solution to the above?
Can we avoid the default result value parameter and if condition check from the above function?
JSBin Link
http://jsbin.com/veyihepulu/1/edit?html,js,console
One approach would be to create a sliding window of adjacent elements using R.aperture. Then for a bit of extra sugar someComplexFn can be wrapped with R.apply to convert the binary function into one that accepts an array of two elements.
Your example would then look something like:
const adjacentMap = R.pipe(R.aperture(2), (R.map(R.apply(someComplexFn))))
Another approach would be to use converge on the array without the last element and the array without the first element.
let locations = [1,2,3,4,5];
const calcRatio = (x, y) => x+y;
// adjacentMap :: Array -> Array
const adjacentMap = R.converge(
R.zipWith(calcRatio),
[ R.init, R.tail]
);
// saveAdjacentMap :: Array -> Array
const saveAdjacentMap = R.cond([
[R.compose(R.lt(1), R.length), adjacentMap ],
[R.T, R.identity]
]);
console.log(saveAdjacentMap(locations));
Your JSBin uses Ramda 0.8.0. Things have changed in the current version 0.24.1.
The following code maybe what you require or can be adapted for your required solution.
const fn = (acc, c, i, a) => {
return !(a[i + 1])
? acc
: acc.concat(c + a[i + 1])
}
const _adjacentMap = (fn, list) => {
return list.reduce(fn, [])
}
const locations = [1,2,3,4,5]
const result = _adjacentMap(fn, locations)
console.log(result)
// => [ 3, 5, 7, 9 ]

Split element into array with reduce()

I have some edges in a graph, which I get with:
const edges = getEdges();
Each edge has an array of labels, which I get with:
const edges = getEdges().map(function (edge, i) {
return (i + 1) + '-' + getLabels(edge).sort().join('-');
});
So if I have 2 edges, each with two labels, I get an array
[
'1-label1-label2',
'2-label1-label2'
]
But what I want is
[
'1-label1',
'2-label2',
'3-label1',
'4-label2'
]
So I guess I need to use Array.reduce().
Using Array#reduce method do something like this.
const edges = getEdges().reduce(function(arr, edge) {
return arr.concat(
getLabels(edge)
.sort()
.map(function(v, i) {
return (arr.length + i) + '-' + v;
})
);
}, []);
Using Array#push method instead of Array#concat method.
const edges = getEdges().reduce(function(arr, edge) {
[].push.apply(arr, getLabels(edge)
.sort()
.map(function(v, i) {
return (arr.length + i) + '-' + v;
})
);
return arr;
}, []);
It doesn't seem to me that either map or reduce is what you actually want, just a nested pair of forEach (although we could shoe-horn a reduce in if we wanted):
const edges = [];
getEdges().forEach(edge => {
getLabels(edge).sort().forEach(label => {
edges.push(`${(edges.length + 1)}-${label}`);
});
});
Live Example:
const getEdges = () =>
[
{labels: ['label2', 'label1']},
{labels: ['label1', 'label2']}
];
const getLabels = edge => edge.labels;
const edges = [];
getEdges().forEach(edge => {
getLabels(edge).sort().forEach(label => {
edges.push(`${(edges.length + 1)}-${label}`);
});
});
console.log(edges);
You can first map the result of getEdges to getLabels, and then flatten the resulting array with Array.prototype.concat and the spread syntax.
const getEdges = () => ['a', 'b'];
const getLabels = () => ['label1', 'label2'];
const edges = [].concat(...getEdges().map(getLabels))
.map((label, i) => `${i + 1}-label`);
console.log(edges);
You can use forEach as below instead of map or reduce.
Working snippet: (ES6)
const getEdges = () => [
'1-label1-label2',
'2-label1-label2'
];
const getLabels = (edge) => edge.split('-').sort();
let edges = [];
getEdges().forEach((edge) => {
getLabels(edge).forEach((label) => {
if(label.indexOf('label') > -1) {
edges.push(edges.length + 1 + '-' + label);
}
});
});
console.log(edges);
Working snippet: (ES5)
function getEdges() {
return [
'1-label1-label2',
'2-label1-label2'
];
}
function getLabels(edge) {
return edge.split('-').sort();
}
var edges = [];
getEdges().forEach(function (edge) {
getLabels(edge).forEach(function (label) {
if(label.indexOf('label') > -1) {
edges.push(edges.length + 1 + '-' + label);
}
});
});
console.log(edges);
You could use a reduce operation on the edges, which takes each edge and adds to a newly constructed array.
Using a forEach on the labels of each edge results in 1..n items, that will be added to this new array. Using the new array (result)'s current length can neatly solve the problem of naming the result item as desired (i.e. from 1 to N).
const res = getEdges().reduce(result, edge => {
const labels = getLabels(edge).sort();
labels.forEach(label => result.push(`${result.length + 1}-${label}`));
return result;
}, []);
NOTE: this solution uses reduce and ES6 features, which is according to mentioned tags.

Categories

Resources