Reduce array in javascript with patern matching - javascript

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)

Related

Grouping array of element based on their value relation ship in javascript

I want to group the array elements with their inner array group met
Input:
const arr = [
[123, 243],
[123, 435],
[736, 987],
[987, 774],
[123, 666],
[774, 999],
[098, 980],
];
Output:
Result = [[123, 243, 435, 666],[736, 987, 774, 999],[098, 980]]
now you can find what i expect ?
I have tried this script but cant complete
function checkVal(array, value) {
return array.map(function (entry, inx, arr) {
if (Array.isArray(entry)) {
return checkVal(entry, value);
}
if (entry === value) {
return arr;
} else {
return null;
}
});
}
function multiDimensionalUnique(arr) {
var uniques = [];
var itemsFound = {};
for (var i = 0, l = arr.length; i < l; i++) {
var stringified = JSON.stringify(arr[i]);
if (itemsFound[stringified]) {
continue;
}
uniques.push(arr[i]);
itemsFound[stringified] = true;
}
return uniques;
}
const arr = [
[123, 243],
[123, 435],
[736, 987],
[987, 774],
[123, 666],
[774, 999],
[098, 980],
];
let firstTake = [];
// Expected Result = [[123, 243, 435, 666],[736, 987, 774, 999],[098, 980]]
let sTake = [];
let i = 0;
arr.forEach((innerArr) => {
if (i == 0) firstTake.push(innerArr);
innerArr.forEach((detailArr) => {
let innerLoopArr = checkVal(arr, detailArr);
innerLoopArr.forEach((innerLoopArrVal) => {
var filtered = innerLoopArrVal.filter(el => el != null);
sTake.push(filtered);
});
});
i++;
});
let clearnArray = sTake.filter(v => v.length != 0);
console.log(multiDimensionalUnique(clearnArray));
Thanks in advance.
You could reduce them into array of objects and then check if some of the element in a tuple is in the reduce accumulator. Not sure if this is the most efficient way, but this should do:
const arr = [
[123, 243],
[123, 435],
[736, 987],
[987, 774],
[123, 666],
[774, 999],
[098, 980],
];
const result = arr.reduce((acc, curr) => {
let newAcc = acc;
const currObj = {
[curr[0]]: null,
[curr[1]]: null,
}
for ([index, value] of newAcc.entries()) {
if (curr.some(c => c in value)) {
newAcc[index] = {
...value,
...currObj
}
return newAcc
}
}
newAcc.push(currObj)
return newAcc
}, []).map(obj => Object.keys(obj))
console.log(result)
Note that you may notice that the 098 becomes 98, this is not because of my code, but because in your original array 098 is detected as number, thus javascript converts it to a valid number (98). If you want to keep it as 098, you should make it array of strings.
Seems like the array is a list of edges of a graph, and you're trying to get a list of sets of nodes in each disjoint graph.
This gets you the result you want:
arr.reduce((result, [a, b]) => {
let g = result.find(g => g.has(a) || g.has(b));
if (!g) result.push(g = new Set());
g.add(a).add(b);
return result;
}, []);
(If you want arrays at the end instead of Sets, you can add .map(s => Array.from(s)) to the end.)
But if you were to add another entry to the array like [736, 666], I'd think the first two sets should be merged. This handles that:
const result = arr.reduce((result, [a, b]) => {
// Get all sets of nodes that contain either node
let [g, ...gs] = [...result].filter(g => g.has(a) || g.has(b));
// If none, make a new one
if (!g) result.add(g = new Set());
// Add both nodes to the set
g.add(a).add(b);
// Merge sets if there's more than one
for (const h of gs) {
for (const e of h) g.add(e);
result.delete(h);
}
return result;
}, new Set());
console.log(Array.from(result, s => Array.from(s)));

Convert array of strings to object with params and values

I have next array with strings:
['val1=123','val2=456']
How I can split it to object with params and values?
{
val1: 123,
val2: 456,
}
const recordStrings = ['val1=123', 'val2=456']
const record = Object.fromEntries(
recordStrings.map(str => str.split('='))
)
console.log(record)
Explanation:
recordStrings.map(str => str.split('=')) returns [[val1, 123], [val2, 456]].
Object.fromEntries(entries) creates an object from an array containing [key, value] tuples.
You can try with reduce method, it's really helpful to convert an array to any others data type like an object, string, number.
const arr = ['val1=123','val2=456'];
const items = arr.reduce((total, item) => {
const [key, value] = item.split('=');
if (key) {
total[key] = value
}
return total;
}, {})
console.log(items);
Split the strings in the array and convert the array to an object:
const res = ['val1=123','val2=456'];
const result = Object.fromEntries(res.map(x => {
const [l, r] = x.split('=');
return [l, +r];
}));
console.log(result);
let obj = {};
let arr = ['val1=123', 'val2=456'];
arr.forEach(i => {
let x = i.split('=');
obj[x[0]] = parseInt(x[1]);
});
console.log(obj);
let arr = ['val1=123', 'val2=456']
let object = {}
for (let i = 0; i < arr.length; i++) {
var split = arr[i].split("=")
object[split[0]] = split[1]
}
console.log(object); // { val1: '123', val2: '456' }
let arr = ['val1=123','val2=456'];
arr.forEach(str => {
let arrStr = str.split('=');
eval(arrStr[0] + '= ' + arrStr[1] + ';');
})
console.log(val1, val2);
OR
let arr = ['val1=123','val2=456'];
arr.forEach(str => {
let arrStr = str.split('=');
window[arrStr[0]] = arrStr[1];
})
console.log(val1, val2);

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>

Get smallest string out of the array with nested arrays

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);

how to remove duplicated object (string)from an array in JavaScript

I have two json arrays with the some same object, what i want is to have a json from these two json which is not includes duplicated value, for example this is my jsons:
json1=["one","two"];
json2=["one","two","three","four"];
the result must be:
result=["three","four"]
do you know how to have to make it, thanks
Solution using a reduce and filter function:
const json1 = ["one","two"];
const json2 = ["one","two","three", "four"];
const result = json2
.reduce((acc, cur) => acc.indexOf(cur) > -1 ? acc : acc.concat(cur), json1)
.filter(item => json1.indexOf(item) === -1 || json2.indexOf(item) === -1);
console.log(result); // ["three", "four"]
You can do it like this. Use Array.prototype.reduce to pick all the elements that are in the first array and not in the second one. Then use it the same way to pick all the elements that are in the second one and not the first one. And finally, merge those two results.
const _removeCommon = (xs, ys) =>
xs.reduce((acc, v) => ys.includes(v) ? acc : acc.concat(v), []);
const removeCommon = (xs, ys) =>
[..._removeCommon(xs, ys), ..._removeCommon(ys, xs)];
const json1=["one","two"];
const json2=["one","two","three","four"];
console.log(removeCommon(json1, json2));
here is the code
this is a function which basically subtracts the 2 array and returns the resultant array
a1 and a2 are the 2 array
a is an empty array which has true value in all the a1[index]
a1 = [1,4,5];
a = [0,1,0,0,1,1];
1 = true
like this
now we check id any a2[index] is present in the a
if a2 = [1,3];
then a[a2[atanyindex]] will be a = [0,1,0,0,1,1];
..........................................................^ 1
and if this happens delete that element
add the remaining elements to the diff array
and the diff array is the subtraction of 2 array aka you required answer
function arr_diff(a1, a2) {
var a = [], diff = [];
for (var i = 0; i < a1.length; i++) {
a[a1[i]] = true;
}
for (var i = 0; i < a2.length; i++) {
if (a[a2[i]]) {
delete a[a2[i]];
} else {
a[a2[i]] = true;
}
}
for (var k in a) {
diff.push(k);
}
return diff;
}
#Jan, try the below code.
It uses filter() and concat() methods defined on arrays.
json1=["one","two"];
json2=["one","two","three","four"];
var arr1 = json1.filter((key) => {
if(json2.indexOf(key) == -1)
return true
else
return false
})
var arr2 = json2.filter((key) => {
if(json1.indexOf(key) == -1)
return true
else
return false
})
result = arr1.concat(arr2)
console.log(result)
You can just use concat and filter pretty simply.
function noDupes(arr1,arr2){
return arr1.concat(arr2).filter((item,index,arr)=>{
if(arr1.includes(item)&&arr2.includes(item))
return false
else
return item
})
}
Here's a solution if you don't know/care which array is longest. Not very short, but easy to read and understand.
function myFilter(arr1, arr2) {
const combined = [...arr1, ...arr2]
const seen = new Set()
combined.forEach(item => {
if (seen.has(item)) {
seen.delete(item)
} else {
seen.add(item)
}
})
return Array.from(seen)
}
First we use the spread operator to easily combine both arrays into a single array.
Then we create a new set which will be like a bucket we toss our values into.
Next we'll use forEach to loop over the combined array and ask
"Have we seen this value before? If so, let's delete it because that means it's not unique; otherwise, let's add it to the set."
Finally, we use Array.from() to turn our set into an array and return it.
You can concatenate the arrays and then filter the duplicates out by comparing indexOf and lastIndexOf for each item:
const json1 = ["one", "two"];
const json2 = ["one", "two", "three", "four"];
const withoutDupes = [...json1, ...json2].filter((item, idx, array) => {
if (array.indexOf(item) === array.lastIndexOf(item)) {
return item;
}
});
console.log(withoutDupes);
This provides you with 3 results based on the type argument.
(1) 'uniq': no duplicates
(2) 'duplicate'
(3) 'not_duplicate'
let json1 = ['one', 'two']
let json2 = ['one', 'two', 'three', 'four']
function uniq_n_shit (arr1, arr2, type) {
if (!type || type === 'uniq' || type === 'unique') {
return [...new Set(arr1.concat(arr2))]
} else if (type === 'duplicate') {
let concat = arr1.concat(arr2)
return concat.filter(function (obj, index, self) {
return index !== self.indexOf(obj)
})
} else if (type === 'not_duplicate') {
let concat = arr1.concat(arr2)
let set = [...new Set(concat)]
let remove = concat.filter(function (obj, index, self) {
return index !== self.indexOf(obj)
})
for (let r = 0; r < remove.length; r++) {
let i = set.indexOf(remove[r]);
if(i !== -1) {
set.splice(i, 1);
}
}
return set
}
}
console.log(uniq_n_shit(json1, json2, null)) // => [ 'one', 'two', 'three', 'four' ]
console.log(uniq_n_shit(json1, json2, 'uniq')) // => [ 'one', 'two', 'three', 'four' ]
console.log(uniq_n_shit(json1, json2, 'duplicate')) // => [ 'one', 'two' ]
console.log(uniq_n_shit(json1, json2, 'not_duplicate')) // => [ 'three', 'four' ]
function myFilter(arraySmall, arrayBig){
var result = [];
for (var i = 0; i < arrayBig.length; i++) {
if (arraySmall.indexOf(arrayBig[i]) === -1){ //ie. if item not exists in arraySmall
result.push(arrayBig[i]);
}
}
return result;
}
You can just call myFilter(json1, json2);

Categories

Resources