I want to add the elements of my new list (newList) to my already existing list (oldList), but before inserting I must verify if my element of my newList already exists in my oldList, in case it does, an extra number must be added.
let newList = [{name: "abc.jpeg"}, {name: "abc.jpeg"}]
let oldList = [{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa.jpeg"},
{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa1.jpeg"},
{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa2.jpeg"}]
const addElement = (colection, new_item, tally) => {
const a = new_item.name.split('.')
const reps = colection.filter(elem => {
var reg = /.*2Fpictures%2F(.*)/
const name = elem.name.match(reg)
if(name)
return name[1].includes(a[0])
}).length
new_item.sname = a[0] + (reps > 0 ? reps : '') + '.' + a[1]
colection.push(new_item)
console.log(colection)
return colection
}
let tally = {}
for (let index = 0; index < newList.length; index++) {
oldList = addElement(oldList, newList[index], tally)
}
the expected result I need is this:
[{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa.jpeg"},
{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa1.jpeg"},
{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa2.jpeg"},
{name: "abc.jpeg"},
{name: "abc1.jpeg"}]
because the three element contain "fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fabc.jpeg" the abc.jpeg
Another approach using what you started with a tally that is a Map and is far more efficient for lookups than using multiple instances of filter()
let newList = [{name: "abc.jpeg"}, {name: "abc.jpeg"},{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa.jpeg"}]
let oldList = [{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa.jpeg"},
{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa1.jpeg"},
{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa2.jpeg"}, {name: "abc.jpeg"}];
const baseName = (str) => str.replace(/\d+\./, '.');
const getNamesCount = arr => arr.reduce((a,{name})=>{
const base = baseName(name);
return a.set(base, (a.get(base)||0)+1)
},new Map())
const mergeLists = (oList, nList) => {
const tally = getNamesCount(oList);
nList.forEach(({name,...rest})=>{
const base = baseName(name);
if(tally.has(base)){
let count = tally.get(base)
name = name.split('.').join(count + '.');
tally.set(base, ++count)
}else{
tally.set(base, 1)
}
oList.push({name,...rest})
})
return oList;
}
console.log(mergeLists(oldList,newList))
//I added some element for testing
let newList = [
{name: "abc.jpeg"},
{name: "abc.jpeg"},
{name:"xxx123.jpg"},
{name:"xxx123.jpg"},
{name:"xxx123.jpg"},
{name:"xxx123.jpg"},
{name:"yyy143.jpg"},
{name:"yyy143.jpg"},
{name:"yyy143.jpg"},
{name:"yyy144.jpg"},
{name:"yyy145.jpg"},
{name:"yyy146.jpg"}
];
let oldList = [
{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa.jpeg"},
{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa1.jpeg"},
{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa2.jpeg"}];
//Creating a Set
const mySet = new Set()
//Fill mySet with files names
for (i=0;i<oldList.length;i++){
mySet.add(oldList[i].name);
}
//Looping newList
for (i=0;i<newList.length;i++){
if (mySet.has(newList[i].name)){
// The point "." position in filename string
pointpos = newList[i].name.lastIndexOf(".");
// File name and file extension
name = newList[i].name.substring(0,pointpos);
ext = newList[i].name.substring(pointpos);
// Getting numbers in file name string from right to left
// initializing cursor pos at the end of the file name string
pos=name.length-1;
//Where to store the numbers of the file name , if any .
numbers=[];
// String numbers 0,1,2,3,4,5,6,7,8,9
// have char codes from 48 to 57
while ( 48 <= name.charCodeAt(pos) && 57 >= name.charCodeAt(pos) && pos >= 0){
//Add values at top of the array , not at bottom.
numbers.unshift(name.charAt(pos));
pos--;
}
//if file name has numbers...
if (numbers.length > 0){
//join numbers and sum 1
num = parseInt(numbers.join(""))+1;
snum = String(num);
// name without numbers part
name = name.substring(0,name.length-numbers.length);
//Sum 1 again while we have a filename in the Set
while (mySet.has(name+snum+ext)){
snum=String(num++);
}
//Here we have a new file name not present in the Set
//So add it to the Set with number and extension
mySet.add( name+snum+ext);
}else{
//The file name is duplicate but
//there are no others with numbers
//so add "1" string to the name and extension
//and add it to the Set
name+="1";
mySet.add(name+ext);
}
}else{
// The filename is not present in oldlist so we can add it in the Set
mySet.add(newList[i].name);
}
}
//Reset oldList array
oldList=[];
//Fill it with objects like it was but with new set items as values
for (let item of mySet) {
oldList.push({name:item})
}
//Show it
console.log(oldList);
Without comments:
const mySet = new Set();
for (i=0;i<oldList.length;i++)mySet.add(oldList[i].name);
for (i=0;i<newList.length;i++){
if (mySet.has(newList[i].name)){
pointpos = newList[i].name.lastIndexOf(".");
name = newList[i].name.substring(0,pointpos);
ext = newList[i].name.substring(pointpos);
pos=name.length-1;
numbers=[];
while ( 48 <= name.charCodeAt(pos) && 57 >= name.charCodeAt(pos) && pos >= 0){
numbers.unshift(name.charAt(pos));
pos--;
}
if (numbers.length > 0){
num = parseInt(numbers.join(""))+1;
snum = String(num);
name = name.substring(0,name.length-numbers.length);
while (mySet.has(name+snum+ext)){
snum=String(num++);
}
mySet.add( name+snum+ext);
}else{
name+="1";
mySet.add(name+ext);
}
}else{
mySet.add(newList[i].name);
}
}
oldList=[];
for (let item of mySet) {
oldList.push({name:item})
}
console.log(oldList);
You can find occurence of the string initially and then append that number to the string.
let newList = [{name: "abc1.jpeg"}, {name: "abc1.jpeg"}]
let oldList = [{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa.jpeg"},
{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa1.jpeg"},
{name:"fs%2FPanel%8SmgM7rhJ4T9j%2Fpictures%2Fa2.jpeg"}]
const addElement = (colection, new_item, tally) => {
const a = new_item.name.split('.')
const occurence = colection.filter(item=>item.name===new_item.name).length;
if(occurence>0){
const firstPart =[...a.slice(0,-1)].join(".");
const digitMatch = firstPart.match(/(\d+)$/);
const digitString = digitMatch && digitMatch[0];
const firstPartWithoutDigit =digitString && firstPart.slice(0,-digitString.length )
const digit = digitString && parseInt(digitString);
const newDigit = (digit || 0) + occurence;
const prefix = firstPartWithoutDigit || firstPart;
new_item.name = prefix+ newDigit +"." +a[a.length-1]
//[...a.slice(0,-1)].join(".") -> Part Before Last .
// occurence-> Number of times filename exists in the collection
//a[a.length-1] -> Last part of string after .
}
const reps = colection.filter(elem => {
var reg = /.*2Fpictures%2F(.*)/
const name = elem.name.match(reg)
if(name)
return name[1].includes(a[0])
}).length
new_item.sname = a[0] + (reps > 0 ? reps : '') + '.' + a[1]
colection.push(new_item)
console.log(colection)
return colection
}
let tally = {}
for (let index = 0; index < newList.length; index++) {
oldList = addElement(oldList, newList[index], tally)
}
Related
I have a JavaScript array of objects which looks like
var myarr = [
{'xx':'2023-01-01,,1'},
{'ss':'2023-01-01,2,1.2'},
{'dd':'2023-01-01,4,'},
{'rr':'2023-01-01,,'},
{'ff':'2023-01-01,,'},
{'gg':'2023-01-01,,'}
];
The array is actually much bigger than that, but I have cut it down for testing purposes, some of my arrays are thousands of lines long
Each object contains a date and two comma-separated values, although I have some rows which contain 3 or 4 comma separate values
What I need to do, is if any blank comma-separated value is found on any row then get the previous comma separated value from that position to a maximum of 2 times going back, although I may need to change that to a bigger number in the future
So with my example, I would get the following output
var myarr = [
{'xx':'2023-01-01,,1.6'},
{'ss':'2023-01-01,2,1.2'},
{'dd':'2023-01-01,4,1.2'},
{'rr':'2023-01-01,4,1.2'},
{'ff':'2023-01-01,4,'},
{'gg':'2023-01-01,,'}
];
I have tried to solve this with
var myarr = [
{'xx':'2023-01-01,,1'},
{'ss':'2023-01-01,2,1.2'},
{'dd':'2023-01-01,4,'},
{'rr':'2023-01-01,,'},
{'ff':'2023-01-01,,'},
{'gg':'2023-01-01,,'}
];
var maxAttempts = 3;
for (var i = 0; i < myarr.length; i++) {
var obj = myarr[i];
var values = Object.values(obj)[0].split(",");
var date = values[0];
var value1 = values[1];
var value2 = values[2];
for (var j = 1; j <= maxAttempts; j++) {
if (!value1) {
value1 = (myarr[i-j] && Object.values(myarr[i-j])[0].split(",")[1]) || " ";
}
if (!value2) {
value2 = (myarr[i-j] && Object.values(myarr[i-j])[0].split(",")[2]) || " ";
}
if (value1 && value2) {
break;
}
}
console.log(date, value1, value2);
for (var k = 3; k < values.length; k++) {
var value = values[k];
console.log(value);
}
}
but it doesn't seem to provide the expected output.
Can someone help me with what might be wrong?
Maybe you can use something like this.
const myarr = [
{ "xx": "2023-01-01,,1" },
{ "ss": "2023-01-01,2,1.2" },
{ "dd": "2023-01-01,4," },
{ "rr": "2023-01-01,," },
{ "ff": "2023-01-01,," },
{ "gg": "2023-01-01,," }
]
function fillInBlanks(arr, maxLookBack) {
return arr.map((obj, index) => {
const key = Object.keys(obj)[0]
const value = Object.values(obj)[0]
.split(",")
.map((x, n) => {
if (x === "" && index > 0) {
for (let i = index - 1; i >= Math.max(0, index - maxLookBack); --i) {
const prev = Object.values(arr[i])[0].split(",")
if (prev[n] !== "") return prev[n]
}
} else return x
})
return Object.fromEntries([
[key, value.join(",")]
])
})
}
fillInBlanks(myarr, 2).forEach(x => console.log(x))
Here's my attempt. This will also work with any number of values per row.
const maxAttempts = 2;
myarr.reduce((modifiedAccumulation, currentObject, index) => {
const [key, csv] = Object.entries(currentObject)[0];
const splitCsv = csv.split(",");
const modifiedCsv = splitCsv
.reduce((fixedArray, currentElement, csvPos) => {
let numberToUse =
currentElement === ""
? myarr
.slice(Math.max(index - maxAttempts, 0), index)
.reduceRight((proposedNum, currentPastObj) => {
if (proposedNum !== "") return proposedNum;
let candidate =
Object.entries(currentPastObj)[0][1].split(",")[csvPos];
return candidate !== "" ? candidate : "";
}, "")
: currentElement;
return [...fixedArray, numberToUse];
}, [])
.join(",");
return [...modifiedAccumulation, { [key]: modifiedCsv }];
}, []);
This approach creates a 'window' array containing the last few entries, which is used to look up prior column values.
const myarr = [{"xx":"2023-01-01,,1"},{"ss":"2023-01-01,2,1.2"},{"dd":"2023-01-01,4,"},{"rr":"2023-01-01,,"},{"ff":"2023-01-01,,"},{"gg":"2023-01-01,,"}]
const windowSize = 2
const w = [], r =
myarr.map(e=>Object.entries(e).flatMap(([k,v])=>[k,...v.split(',')]))
.map(a=>(
w.unshift(a) > windowSize+1 && w.pop(),
a.map((_,i)=>w.find(x=>x[i])?.[i])
)).map(([k,...v])=>[k,v.join()]
).map(i=>Object.fromEntries([i]))
console.log(r)
let wordsArray;
let indexArray = [];
let index;
let myMap = new Map();
const Storage = function(userInput){
wordsArray = userInput.split(' ');
//remove ',' and '.'
for( let i = 0; i < wordsArray.length ; i ++){
if(wordsArray[i].endsWith(',') || wordsArray[i].endsWith('.') || wordsArray[i].endsWith('!') || wordsArray[i].endsWith(':')) {
let temp = wordsArray[i];
wordsArray[i] = temp.slice(0, temp.length - 1 );
}
//ToLowerCase
let temp = wordsArray[i]
wordsArray[i] = temp.toLowerCase();
//IndexCreation
let letter = wordsArray[i].slice(0,1);
indexArray.push(letter);
//Add to Array
myMap.set(letter,wordsArray[i]);
}
console.log(myMap);
}
Storage("Hello, my name is Gleb. My hobby is to learn Javascript");
Expected output h stands for hello and hobby, but actually it contains only last word - hobby. How can i add word to index, instead of repleasing?
The elements of your "index" map should be arrays rather than strings. When adding a word, check if the first letter already exists in the map, and if not, initialize it to an empty array. Then, add a word to map[letter]:
let index = new Map
word = 'hello'
letter = word[0]
if (!index.has(letter))
index.set(letter, [])
index.get(letter).push(word)
word = 'hobby'
letter = word[0]
if (!index.has(letter))
index.set(letter, [])
index.get(letter).push(word)
console.log([...index])
// Add to Array
if (myMap.has(letter)) {
let temp = myMap.get(letter);
temp = [temp,wordsArray[i]]
myMap.set(letter, temp)
} else {
myMap.set(letter, wordsArray[i]);
}
I have an array of strings like this:
const strings = [
"author:app:1.0.0",
"author:app:1.0.1",
"author:app2:1.0.0",
"author:app2:1.0.2",
"author:app3:1.0.1"
];
And I want to filter them so that only the ones that have the latest versions for the given "author:name" are left, thus removing ones that are not the latest (i.e. the "1.0.1" ones).
My expected result is this:
const filteredStrings = [
"author:app:1.0.1",
"author:app2:1.0.2",
"author:app3:1.0.1"
];
Any way to do this simply?
You can do it with two loops first one find new ones second one check which is bigger
const strings = [
"author:app:1.0.0",
"author:app:1.0.1",
"author:app2:1.0.0",
"author:app2:1.0.2",
"author:app3:1.0.1"
];
filteredones = [];
strings.forEach(element => {
var arr = element.split(":");
var isnew = true;
var found = filteredones.find(function(element2) {
var x = element2.split(":");
return x[1] == arr[1] && x[0] == arr[0]
});
if (found == undefined) {
filteredones.push(element);
}
});
for (var i = 0; i < filteredones.length; i++) {
element = filteredones[i];
var arr = element.split(":");
var isnew = true;
var found = strings.find(function(element2) {
var x = element2.split(":");
return x[1] == arr[1] && x[0] == arr[0] && x[2] > arr[2]
});
if (found != undefined) {
filteredones[i] = found;
}
};
console.log(filteredones);
you can check the value in the last index of the string in each of the elements of the array and if they qualify as a latest one put it to a new array.
You can use an object to store the key/version pairs, and convert to appropriate output on the end. The version comparison can be any of those found here: How to compare software version number using js? (only number)
result = {};
for (var s of input) {
// parts = ["author", "appname", "version"]
var parts = s.split(":");
var i = parts[0] + ":" + parts[1];
if (!result[i] || compareVersion(parts[2], result[i]))
// If not present or version is greater
result[i] = parts[2]; // Add to result
}
result = Object.keys(result).map(k => k + ":" + result[k])
Working demo: https://codepen.io/bortao/pen/LYVmagK
Build an object with keys as app name.
getValue method is calculate the version value so that to compare.
Update object value, when you see the version is recent (value is big).
const strings = [
"author:app:1.0.0",
"author:app:1.0.1",
"author:app2:1.0.0",
"author:app2:1.0.2",
"author:app3:1.0.1"
];
const filter = data => {
const res = {};
const getValue = item =>
item
.split(":")[2]
.split(".")
.reduceRight((acc, curr, i) => acc + curr * Math.pow(10, i), 0);
data.forEach(item => {
const app = item
.split(":")
.slice(0, 2)
.join(":");
if (!res[app] || (app in res && getValue(item) > getValue(res[app]))) {
res[app] = item;
}
});
return Object.values(res);
};
console.log(filter(strings));
I was writing code and came into this problem,
You have a specific string which is in this form:
d ae2 n s
now we have to decode this in a specific way,
Split it into different parts by spaces to make an array like ["d","ae2","n","s"]
Evaluate each element of the array and find out if there is a number in it.
If there is a number then repeat the string the number of times.
Add it into the array and continue.
So the output array should be
["d","ae","ae","n","s"]
I have already tried a lot but got nothing
I have used this code earlier but it ends on the second string:
var str = "d ae2 n s"
var res = str.split(" ");
alert(res.length);
for(var x = 0; x < res.length; x++ ){
var std = res[x];
var fun = checkNum(std);
if(fun === true){
var numbers = str.match(/\d+/g).map(Number);
var index = res.indexOf(std);
var result = std.replace(/[0-9]/g, '');
var res2 = result.repeat(numbers);
res[index] = res2;
}
else{
continue;
}
for(var i = 0; i < res.length; i++ ){
console.log(res[x]);
}
}
function checkNum(t){
return /\d/.test(t);
}
// I am a terible coder :/
expected input : d ae2 n s
expected output : ["d","ae","ae","n","s"]
Using fill() and flatMap() methods and
regex replace
/[^0-9]/ - all non numerical chars
/[0-9]/ - all numerical chars
var str = 'd ae2 n s'
var res = str
.split(' ')
.flatMap(i =>
Array(+i.replace(/[^0-9]/g, '') || 1)
.fill(i.replace(/[0-9]/g, ''))
)
console.log(res)
You can simply loop over your array and populate an other array that will hold your result after checking for a number :
const results = [];
"d ae2 n s".split(' ').forEach(token => {
const match = token.match(/\d+/);
if (match) {
const newStr = token.split(/\d/)[0];
for (let i = 0; i < match[0]; i++) {
results.push(newStr);
}
} else {
results.push(token)
}
})
console.log(results);
You can check Seblor's answer for optimized logic. I have modified your code so that it will be easy for you to understand where you went wrong while doing this. I have added comments to your code where I have changed things:
var str = "d ae2 n s"
var res = str.split(" ");
// create a variable to store the output.
var output = [];
for(var x = 0; x < res.length; x++ ){
var std = res[x];
var fun = checkNum(std);
if(fun === true){
// map returns an array, so take the first element, it will be your number.
var numbers = str.match(/\d+/g).map(Number)[0];
var index = res.indexOf(std);
var result = std.replace(/[0-9]/g, '');
// instead of doing the repeat and updating the current index,
// push the result, i.e. the current string to be repeated "numbers" times into
// the output array.
for (var i = 0; i < numbers; i++) {
output.push(result)
}
}
else{
// if does not contain any number, push the current item to ouput
output.push (std);
continue;
}
}
function checkNum(t){
return /\d/.test(t);
}
console.log(output);
You can do:
const str1 = 'd ae2 n s';
const str2 = 'e d aefg4 m n s';
const regex = /\d+/;
const getResult = input => input.split(' ').reduce((a, c) => {
const n = c.match(regex);
return n
? [...a.concat(c.replace(n, ' ').repeat(n).trim().split(' '))]
: [...a, c];
}, []);
console.log(getResult(str1));
console.log(getResult(str2));
you can use the Array prototype reduce and filter
const input = 'd ae2 n s';
const output = input.split(' ').reduce((memory, current) => {
const numberIndex = current.split('').findIndex(c => !isNaN(c));
const newCurrent = current.split('').filter((_, index) => index !== numberIndex).join('');
if(numberIndex !== -1) {
for(let i = 0; i < parseInt(current[numberIndex]); i++) {
memory.push(newCurrent);
}
} else {
memory.push(current);
}
return memory;
}, []);
console.log(output);
Hope this helped
You can try with following:
let str = "d ae2 n s"
let split = str.split(" ")
let rx = new RegExp("[0-9]")
let res = [];
split.forEach(s => {
if(rx.exec(s) !== null) {
let rxResult = rx.exec(s)
let count = rxResult[0];
let matchIdx = rxResult[1];
for(let i = 0; i < count; i++) {
res.push(s.replace(count, ""))
}
} else {
res.push(s);
}
})
I have to refactor my redux functions
In these 3 cases in am renaming the variable in a different way and i do not want this
See the example
case 'INCREMENT_LIKES' :
const index = action.index;
return [
...state.slice(0,index), // before the one we are updating
{...state[index], likes: state[index].likes + 1},
...state.slice(index + 1), // after the one we are updating
]
case 'INCREMENT_COORDINATES' :
const a = action.index;
let string = state[a].d || state[a].d2;
let array = string.split(" ");
let max = array.length;
let last = max - 2;
let i = (state[a].index || 3) + 1;
if ( i === last ) i = 3;
if (array[i] !== 'M' && array[i] !== 'L') array[i] = parseInt(array[i]) + 20;
return [
...state.slice(0,a), // before the one we are updating
{...state[a], d: array.join(' '), index: i, d2: array.join(' '), index: i }, // updating
...state.slice(a + 1), // after the one we are updating
]
case 'INCREMENT_VIEWBOX' :
const count = action.index;
let string2 = state[count].viewBox;
let array2 = string2.split(" ");
let max2 = array2.length;
let i2 = (state[count].index || 2) + 1;
if ( i2 === max2 ) i2 = 2;
array2[i2] = parseInt(array2[i2]) - 20;
return [
...state.slice(0,count), // before the one we are updating
{...state[count], viewBox: array2.join(' '), index: i2},
...state.slice(count + 1), // after the one we are updating
]
default:
return state;
These are the different variable names in the 3 cases
const index = action.index;
const a = action.index;
const count = action.index;
Same for
let string = state[a].d || state[a].d2;
let string2 = state[count].viewBox;
and
let array = string.split(" ");
let array2 = string2.split(" ");
How can i reuse the same variable name for all the 3 cases?
I mean using the same name for the 3 different cases like:
const index - let string - let array
You can add a scope to each case by wrapping them in curly brackets:
case 'INCREMENT_LIKES' : {
const index = action.index;
return [
...state.slice(0,index), // before the one we are updating
{...state[index], likes: state[index].likes + 1},
...state.slice(index + 1), // after the one we are updating
]
}
case 'SOME_OTHER_ACTION_TYPE' : {
console.log(index) // -> undefined
}