Get smallest string out of the array with nested arrays - javascript

I am trying to get the smallest string out of every nested array in the following array object
let data = ["test string", ["abcd", "efj", ["hijklm", ["op"], "hijk", "hijklmn", ["op", "opq"]]]]
I have tried the code but it gives me stackoverflow error,Any help please
let data = ["test string", ["abcd", "efj", ["hijklm", ["op"], "hijk", "hijklmn", ["op", "opq"]]]]
let smallest = []
function getSmallest(data) {
data.forEach((ele, i) => {
if (typeof ele == "string") {
smallest.push(ele);
} else if (typeof ele == "object") {
// removing the array first
let _data = JSON.parse(JSON.stringify(data));
let only_array = _data.splice(i, 1);
getSmallest(only_array)
// now data contains only strings
//finding the smalles string from array
let small = _data.filter(v => typeof v === 'string')
.reduce((a, v) => a && a.length <= v.length ? a : v, '')
smallest.push(small);
}
});
}
getSmallest(data);
console.log(smallest)
Required result -Smallest in every array (nested one as well)
["test string", "efj", "hijk", "op", "op"]

You could take a recursive approach.
const
smallest = array => array
.reduce((r, value) => {
if (Array.isArray(value)) r.push(...smallest(value));
else if (!r[0].length || r[0][0].length > value.length) r[0][0] = value;
return r;
}, [[]])
.flat(),
data = ["test string", ["abcd", "efj", ["hijklm", ["op"], "hijk", "hijklmn", ["op", "opq"]]]],
result = smallest(data);
console.log(result);

You can use .reduce, here is an example:
const data = ["test string", ["abcd", "efj", ["hijklm", ["op"], "hijk", "hijklmn", ["op", "opq"]]]]
const sortingWeight = (v) => Array.isArray(v) ? Infinity : v.length
const reducer = (acc, cur, i, arr) => {
if (Array.isArray(cur)) {
acc = [...acc, ...cur.reduce(reducer, [])];
} else if (i === 0) {
const smallestAtThisLevel = arr.sort((a, b) => {
a = sortingWeight(a);
b = sortingWeight(b);
return a - b;
})[0];
if (!Array.isArray(smallestAtThisLevel)) {
acc.push(smallestAtThisLevel);
}
}
return acc;
}
const result = data.reduce(reducer, []);
console.log(result);

An solution with recursive function and reduce
let data = ["test string", ["abcd", "efj", ["hijklm", ["op"], "hijk", "hijklmn", ["op", "opq"]]]]
let result = [];
function getSmallestString(inp) {
let smallest = inp.reduce((a,v) => {
if(Array.isArray(v)) {
getSmallestString(v);
return a;
}
if(!a || v.length < a.length) return v;
return a;
}, null)
result.unshift(smallest);
}
getSmallestString(data);
console.log(result);

This version works by accumulating the results into an array (initially empty) that is passed down through the recursive layers:
// (sup-optimal) helper function to split the array by type
// if you want optimal, use the underscore version
const partition = (a, pred) => [ a.filter(pred), a.filter(e => !pred(e)) ];
// helper function for reduce
const shorter = (a, b) => (a.length < b.length) ? a : b;
function getSmallest(data, result = []) {
// split the array into strings and subarrays
const [strings, sub] = partition(data, e => typeof e === 'string');
// add the shortest string, if any
if (strings.length) {
result.push(strings.reduce(shorter));
}
// recurse through sub-arrays, if any
if (sub.length) {
sub.forEach(e => getSmallest(e, result));
}
return result;
}

let data = ["test string", ["abcd", "efj", ["hijklm", ["op"], "hijk", "hijklmn", ["op", "opq"]]]]
const shorter = (left, right) => left.length <= right.length ? left : right;
const smallest = data.flat(Infinity).reduce(shorter)
console.log(smallest);

Just vanilla javascript
let data = [
"test string",
["abcd", "efj", ["hijklm", ["op"], "hijk", "hijklmn", ["op", "opq"]]],
];
let k = 0;
let result = [];
function smallestStringSolve(arr) {
let temp_arr = [];
let smallest_string_len = Infinity;
let smallest_string = "";
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] == "string") {
if (arr[i].length < smallest_string_len) {
smallest_string_len = arr[i].length;
smallest_string = arr[i];
}
}else if (Array.isArray(arr[i])){
temp_arr.push(arr[i]);
}
}
if(smallest_string)
result[k++] = smallest_string;
if (temp_arr.length){
for(let i=0; i<temp_arr.length; i++)
smallestStringSolve(temp_arr[i]);
}
return;
}
smallestStringSolve(data);
console.log(result);

Related

Javascript, getting past values for an array of objects

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)

Reduce array in javascript with patern matching

How would you reduce items from array arr into reducedArray by removing items that first two characters match with strings from array generalCase and if they encountered, then an asterisk is added to indicated that this string was encountered on array arr.
let arr =["ADB", "AB1", "BD1", "BD4", "AB3", "BP9", "BPX","STR", "ABS"]
let generalCase = ["AB", "BD", "BP"]
let reducedArray = []
arr.forEach( item => {
if (item.startsWith("AB") && !reducedArray.includes("AB*")) {
reducedArray.push("AB*");
} else if (item.startsWith("BD") && !reducedArray.includes("BD*")) {
reducedArray.push("BD*")
} else if (item.startsWith("BP") && !reducedArray.includes("BP*")) {
reducedArray.push("BP*")
} else if (item === "STR") {
reducedArray.push("STR")
} else if (!reducedArray.includes(item)) {
reducedArray.push(item)
}
})
// Desired result: ["ADB", "AB*", "BD*", "BP*", "STR"]
console.log(reducedArray) // ["ADB", "AB*", "BD*", "BD4", "AB3", "BP*", "BPX", "STR", "ABS"]
You could create a regex from the generalCase array to test if any of the strings in arr start with any of them.
In this case, it creates a regex like this:
/^(?:AB|BD|BP)/
Here's a snippet:
const arr = ["ADB", "AB1", "BD1", "BD4", "AB3", "BP9", "BPX", "STR", "ABS"],
generalCase = ["AB", "BD", "BP"],
regex = new RegExp(`^(?:${generalCase.join("|")})`),
set = new Set;
for (const str of arr) {
if (regex.test(str))
set.add(str.slice(0, 2) + "*")
else
set.add(str)
}
console.log([...set])
Use reduce():
const arr = ["ADB", "AB1", "BD1", "BD4", "AB3", "BP9", "BPX","STR", "ABS"];
const generalCase = ["AB", "BD", "BP"];
const reducedArray = arr.reduce((p, c) => {
const firsttwo = c.substring(0,2);
const toPush = (generalCase.includes(firsttwo)) ? (firsttwo + '*') : c;
if (!p.includes(toPush)) {
p.push(toPush);
}
return p;
}, []);
console.log(reducedArray);
Result:
[
"ADB",
"AB*",
"BD*",
"BP*",
"STR"
]
Iterate normally, and deduplicate the result in the end:
let arr =["ADB", "AB1", "BD1", "BD4", "AB3", "BP9", "BPX","STR", "ABS"]
let generalCase = ["AB", "BD", "BP"]
let reducedArray = arr.map(item => {
for (let x of generalCase)
if (item.startsWith(x))
return x + '*'
return item
})
reducedArray = [...new Set(reducedArray)]
console.log(reducedArray)

Script to group elements where every character is same to each other

For input:
["abc","def","okg","fed","bca"]
expected output should be:
["abc","bca"],["def","fed"],["okg"]
here "abc", "bca" and "def", "fed" contains same character and "okg" there is no element which contains these character
const arr = ["abc", "def", "okg", "fed", "bca"];
let find = (arr) => {
let res = [];
for (let i = 0; i < arr.length; i++) {
for (let j = 1; j < arr.length; j++) {
if (arr[i].search(arr[j])) {
res.push(arr[j]);
}
}
}
return res;
}
console.log(find(arr))
A reduce will do the trick - it seems the shortest code here (apart from the one using lodash)
const arr = ["abc", "def", "okg", "fed", "bca"],
res = Object.values(arr.reduce((acc, ele) => {
const key = ele.split("").sort();
(acc[key] = acc[key] || []).push(ele)
return acc
}, {}))
console.log(res)
.search returns a number indicating the index of where the match was found. Check that the result isn't -1 instead of checking that the result is truthy. But...
.search isn't the right tool here anyway, because it won't find different combinations of the same character. You need a different approach. One way would be to create an object whose keys are the characters found, and the values are the number of occurrences, then use a sorted representation of that object as a key. For example, have both abc and bca turn into something like:
a,1-b,1-c,1
Iterate through the input array, generating a key for each string, and putting the string on an object with that key. At the end, take the object's values.
const strToKey = (str) => {
const grouped = {};
for (const char of str) {
grouped[char] = (grouped[char] || 0) + 1;
}
return Object.entries(grouped)
.sort((a, b) => a[0].localeCompare(b[0]))
.join('-');
};
let find = (arr) => {
const grouped = {};
for (const str of arr) {
const key = strToKey(str);
grouped[key] ??= [];
grouped[key].push(str);
}
return Object.values(grouped);
}
console.log(find(["abc", "def", "okg", "fed", "bca"]));
Another option, when creating the keys, instead of sorting the object afterwards, you could sort the string first:
const strToKey = (str) => {
const grouped = {};
for (const char of [...str].sort()) {
grouped[char] = (grouped[char] || 0) + 1;
}
return Object.entries(grouped).join('-');
};
let find = (arr) => {
const grouped = {};
for (const str of arr) {
const key = strToKey(str);
grouped[key] ??= [];
grouped[key].push(str);
}
return Object.values(grouped);
}
console.log(find(["abc", "def", "okg", "fed", "bca"]));
const input = ["abc","def","okg","fed","bca"]
function getSortedString (str) {
return [...str].sort().join('');
};
function groupBy(input) {
const grouped = [];
while(input.length) {
const nextInput = [];
const first = input[0];
const matched = [first];
for (let i = 1; i < input.length; i++) {
if(getSortedString(first) === getSortedString(input[i])) {
matched.push(input[i])
} else {
nextInput.push(input[i])
}
}
input = nextInput;
grouped.push(matched);
}
console.log(grouped);
}
groupBy(input);
Using Object.values and groupBy (from lodash), you can get a straightforward solution:
You group your array elements by their "sorted" form and then use Object.values to get the output array.
const arr = ["abc", "def", "okg", "fed", "bca"];
const sortString = (str) => str.split("").sort().join("")
const result = Object.values(_.groupBy(arr, sortString));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

Create nested object from query string in Javascript

I have the following query string:
student.name.firstname=Foo&student.name.lastname=Bar&student.address=My%20Street
How to convert to nested object like this:
{
student:{
name:{
firstname: "Foo",
lastname: "Bar"
},
address: "My Street"
}
}
I have tried the following code but something is wrong:
function convertQueryToMap(query) {
var params = {};
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
var subpairs;
if (pair[0].includes('.')) {
subpairs = pair[0].split('.');
var object = {};
subpairs.reduce(function(o, s, i) {
if (i === subpairs.length-1) {
return o[s] = decodeURIComponent(pair[1]);
} else {
return o[s] = {};
}
}, object);
}
}
return params;
}
Do you know a solution?
You can use reduce method to create nested structure and split method to split the query first on parts based on & and also to get key and value from each part.
const query = 'student.name.firstname=Foo&student.name.lastname=Bar&student.address=My%20Street'
const toObject = string => {
return string.split('&').reduce((r, s) => {
const [key, val] = s.split('=');
key.split('.').reduce((a, e, i, ar) => {
return a[e] || (a[e] = (ar[i + 1] ? {} : val.replace(/%20/g, ' ')))
}, r);
return r;
}, {})
}
const result = toObject(query);
console.log(result)
You could decode the string, split for parts, then for keys and value and assign the value to the nested object.
function setValue(object, keys, value) {
var last = keys.pop();
keys.reduce((o, k) => o[k] = o[k] || {}, object)[last] = value;
}
var string = 'student.name.firstname=Foo&student.name.lastname=Bar&user.address=My%20Street',
result = {};
decodeURI(string).split('&').forEach(s => {
var [key, value] = s.split('=');
setValue(result, key.split('.'), value);
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Create an arrays inside of another (main) array out of separated values

Problem
I have a string of numerical values separated by commas, and I want to include them in an array, and also each pair of them to be an array nested inside of the main array to be my drawing vertices.
How do I solve this problem?
Input:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
what I want them to be is:
Output:
var V_array = [[24,13],[47,20],[33,9],[68,18],[99,14],[150,33],[33,33],[34,15],[91,10]];
You could Split on every second comma in javascript and map the splitted pairs by converting the values to number.
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10",
result = vertices.match(/[^,]+,[^,]+/g).map(s => s.split(',').map(Number));
console.log(result);
You can use the function reduce which operates over the splitted-string and check for the mod of each index.
let str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
let result = str.split(',').reduce((a, s, i) => {
a.curr.push(Number(s));
if ((i + 1) % 2 === 0) {
a.arr.push(a.curr);
a.curr = [];
}
return a;
}, {arr: [], curr: []}).arr;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can split string into array and use reduce method. Take a look at the code below
const vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const numbers = vertices.split(',').map(Number)
const res = numbers
.reduce((acc, number, index, srcArray) => {
if (index % 2) {
return acc
}
return [
...acc,
[ number, srcArray[index + 1] ],
]
}, [])
console.log(res)
My two cents :) [new version]
let
str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10",
pair = [],
triplet = [];
JSON.parse(`[${str}]`).forEach((e,i)=>{pair.push( (i%2)?[pair.pop(),e]:e)})
console.log ( 'pair:', JSON.stringify(pair) )
// bonus => same idea for triplet :
JSON.parse(`[${str}]`).forEach((e,i)=>{
if ( (i%3)===2 ) triplet.push( [triplet.shift(),triplet.pop(),e] )
else if ( (i%3)===0 ) triplet.unshift(e)
else triplet.push(e)
})
console.log ( 'triplet:', JSON.stringify(triplet) )
You can use exec and JSON.parse
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
var array1;
var reg = /[^,]+,[^,]+/g
let op = []
while((array1 = reg.exec(vertices))!== null){
op.push(JSON.parse(`[${array1[0]}]`))
}
console.log(op)
Split on the , and use Array.reduce to group the pair into a new 2-D array:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const pair = vertices.split(",").reduce((acc, ele, idx, arr) => {
if(idx === 0 || idx%2 === 0) {acc.push([+ele, +arr[idx + 1]]);}
return acc;
}, []);
console.log(pair);
Same can be done using Array.map, if the index is odd skip the element and filter out the undefined elements:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const pair = vertices.split(",").map((ele, idx, arr) => (idx === 0 || idx%2 === 0) ? [+ele, +arr[idx + 1]] : undefined).filter(e => e);
console.log(pair);
My two cents :)
( thanks to Code Maniac for the idea of using JSON.parse )
let str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
let result = JSON.parse(`[${str}]`).reduce((acc, cur, i) => {
if (i===1) return [[acc,cur]]
if (i%2) acc.push( [acc.pop(), cur] )
else acc.push( cur )
return acc
});
console.log ( result )
Here is my solution.
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
vertices = vertices.split(",");
function convertToMultiArray (arr, length) {
var nArr = [];
while(arr.length > 0) {
nArr.push(arr.splice(0,length));
}
return nArr;
}
const res = convertToMultiArray(vertices, 2);
console.log('res', res);

Categories

Resources