Comparison based on valid string combination in javascript - javascript

I have selected values on screen and their value are stored in two variables.
var uval = '100';
var eval = '5';
There are 2 combinations that have values:
let combination1= 'u:100;e:1,4,5,10'
let combination2 = 'u:1000;e:120,400,500,1000'
I want to check if uval and eval are present in any combination and set some Boolean to true otherwise it'll be false.
uval will be compared to u in that combination and eval with e in that combination.
Boolean in this example will be true.
If uval='50'; eval='5' then it'll be false.
I tried using a for loop using split(';') and compare each u with uval and e with eval.
I am looking for some different approach for this apart from using traditional for loops everytime.

you can do something like this
basically I created a function that transform you combination into an object and I used that object inside the check function
const combination1= 'u:100;e:1,4,5,10'
const combination2 = 'u:1000;e:120,400,500,1000'
const uval = '100';
const eval = '5';
const toObject = combination => Object.fromEntries(
combination.split(';').map(c => c.split(':')).map(([k, v]) => [k, v.split(',')])
)
const check = (combination, key , value ) => (toObject(combination)[key] || []).includes(value)
const checkEval = (combination, eval) => check(combination, 'e', eval)
const checkUval = (combination, uval) => check(combination, 'u', uval)
console.log(checkEval(combination1, eval))
console.log(checkEval(combination2, eval))
console.log(checkUval(combination1, uval))
console.log(checkUval(combination2, uval))

I've created a function named getKeyValuePairs that returns key value pairs for a given combination.
So, if we call getKeyValuePairs with the combination "u:100;e:1,4,5,10" it would return the following array of key value pairs:
[
[ 'u', [ '100' ] ],
[ 'e', [ '1', '4', '5', '10' ] ]
]
Then I've created another function named groupCombinations that merges all the combinations passed to it into a single object.
So, if we call groupCombinations with combinations "u:100;e:1,4,5,10" and "u:1000;e:120,400,500,1000", it would return the following object:
{
u: Set(2) {"100", "1000"},
e: Set(8) {"1", "4", "5", "10", "120", "400", "500", "1000"}
}
Finally I've grouped all the combinations together using groupCombinations and then checked if the group contains uVal and eVal.
const getKeyValuePairs = (combination) =>
combination
.split(";")
.map((str) => str.split(":"))
.map(([k, v]) => [k, v.split(",")]);
const groupCombinations = (...combinations) =>
Object.fromEntries(
Object.entries(
combinations.reduce(
(group, combination) => (
getKeyValuePairs(combination).forEach(([k, v]) =>
(group[k] ??= []).push(...v)
),
group
),
{}
)
).map(([k, v]) => [k, new Set(v)])
);
const uVal = "100";
const eVal = "5";
const combination1 = "u:100;e:1,4,5,10";
const combination2 = "u:1000;e:120,400,500,1000";
const allCombinations = groupCombinations(combination1, combination2);
const isUVal = allCombinations["u"].has(uVal);
const isEVal = allCombinations["e"].has(eVal);
const isBoth = isUVal && isEVal;
console.log(isBoth);

Related

Loop through array for each value from another array

How can I loop through this array:
const counts = [
"900,google.com",
"60,mail.yahoo.com",
"10,mobile.sports.yahoo.com",
"40,sports.yahoo.com",
"300,yahoo.com",
"10,stackoverflow.com",
"20,overflow.com",
"5,com.com",
"2,en.wikipedia.org",
"1,m.wikipedia.org",
"1,mobile.sports",
"1,google.co.uk",
];
taking each value from this array?
const uniqueDomains = [
'google.com',
'com',
'mail.yahoo.com',
'yahoo.com',
'mobile.sports.yahoo.com',
'sports.yahoo.com',
'stackoverflow.com',
'overflow.com',
'com.com',
'en.wikipedia.org',
'wikipedia.org',
'org',
'm.wikipedia.org',
'mobile.sports',
'sports',
'google.co.uk',
'co.uk',
'uk'
]
I need to find out if string from counts array includes string from uniqueDomains array.
Then push it to the empty object as a key value pairs, where value
is going to be the number in the beginning of the each string from counts array.
I tried this code but it give me wrong result in my object's values(since I am looping twice)
I need kind of avoid looping twice, but I am not sure how.
Example com is mentioned 8 time in counts array, which means result should be this {com: 1345}
Here is my code:
const finalObject = {}
uniqueDomains.forEach((dom) => {
counts.forEach((cnt) => {
if (cnt.includes(dom)) {
const num = parseInt(cnt);
sumArr.push(num);
const res = sumArr.reduce((acc, cur) => {
return acc + cur;
});
finalObject[dom] = res;
}
});
});
Theres not really any avoiding looping twice (at least), but you can certainly make your code a bit easieer by first turning the count array into an array of val & domain separately.
const countIdx = counts.map(x => {
const [val,domain] = x.split(",");
return {val:parseInt(val,10), domain}
});
Then its just a case of looping the uniqueDomain array and finding all the domaion which match and summing up the val
const result = uniqueDomains.reduce( (res, d) => {
const count = countIdx.filter(x => x.domain.includes(d)).reduce( (acc,x) => acc + x.val,0);
return {...res, [d]:count}
},{});
Live example follows:
const counts = [
"900,google.com",
"60,mail.yahoo.com",
"10,mobile.sports.yahoo.com",
"40,sports.yahoo.com",
"300,yahoo.com",
"10,stackoverflow.com",
"20,overflow.com",
"5,com.com",
"2,en.wikipedia.org",
"1,m.wikipedia.org",
"1,mobile.sports",
"1,google.co.uk",
];
const uniqueDomains = [
'google.com',
'com',
'mail.yahoo.com',
'yahoo.com',
'mobile.sports.yahoo.com',
'sports.yahoo.com',
'stackoverflow.com',
'overflow.com',
'com.com',
'en.wikipedia.org',
'wikipedia.org',
'org',
'm.wikipedia.org',
'mobile.sports',
'sports',
'google.co.uk',
'co.uk',
'uk'
]
const countIdx = counts.map(x => {
const [val,domain] = x.split(",");
return {val:parseInt(val,10), domain}
});
const result = uniqueDomains.reduce( (res, d) => {
const count = countIdx.filter(x => x.domain.includes(d)).reduce( (acc,x) => acc + x.val,0);
return {...res, [d]:count}
},{});
console.log(result);
Maybe try something like:
let finalObject = {}
uniqueDomains.forEach((dom) => {
finalObject[dom] = 0;
counts.forEach((cnt) => {
if (cnt.includes(dom)) {
finalObject[dom] += parseInt(cnt);
}
});
});

Combine arrays of objects by object index [duplicate]

This question already has an answer here:
How to merge each object within arrays by index?
(1 answer)
Closed 3 months ago.
I am filtering an array for every value the is the same as the key provided. Im certain there is a one shot reduce method someone better than me can condense this down to, but alas filter map filter map.
So I submit to an array an object that says [{k:v}, {k2:otherv}] and find all the elements that are not that and then return those object keys.
The code below returns:
[
{k: v1},
{k: v2},
{k: v3}
]
[
{k2: v4},
{k2: v5},
{k2: v6}
]
]
And obviously to map over it correctly id like it to look like
[{k:v1, k2:v4}, {k:v2,k2:v5}, {k:v3, k2:v6}]
I've tried several examples from:
How can I merge two object arrays by index in JavaScript?
and
Combine same-index objects of two arrays
but short of writing every object key possible into each of these, none of what I've tried works.
const blogkeys = cont
.filter((k) => k.type === "blogs")
.map(({ key, content }) => {
if (key.includes(".")) {
let objkey = key.substr(key.indexOf(".") + 1, key.length);
let obj = { [objkey]: content };
let arrName = key.substr(0, key.indexOf("."));
let pushedObj = { [arrName]: [{ ...obj }] };
return pushedObj;
} else {
let obj = { [key]: content };
return obj;
}
});
this creates the keys we are looking for in the parent array
const everyOtherBlog = blogkeys.map((blogkey) => {
const returned = blogs
.filter(
(f) =>
!JSON.stringify(f).includes(
JSON.stringify(blogkey).replace("{", "").replace("}", "")
)
)
.map(({ _doc }) => {
let obj = {};
Object.keys(_doc)
.filter((f) => f === Object.keys(blogkey)[0])
.map((a) => {
obj = Object.assign(obj, { [a]: _doc[a] });
return obj;
});
return obj[0];
});
return returned;
});
This returns the data set you see.
Here is what blogkeys looks like :
[0] [
[0] { title: ' stuff' },
[0] {
[0] p1: ' stuff '
[0] }
[0] ]
which is made from
{
[0] _id: '606a4049d4812928986afc10',
[0] contentId: '60443ced4e233336f8306b5b',
[0] type: 'blogs',
[0] key: 'title',
[0] content: 'stuff'
[0] },
and a blog looks something like
{
title: '',
p1:''
}
Everyone here provided alot of cool stuff that ended up not helping me because of how i was feeding the data in, when i fixed that i realized i didnt need any fancy zips just good old object.fromEntries. Ill leave this up though cause some of these are very interesting.
Any help would be great
two arrays
You can use map to implement zip and then map again to perform your tranform. This solution works for only two input arrays -
const zip = (a, b) =>
a.map((x, i) => [x, b[i]])
const foo =
[{a:1},{a:2},{a:3}]
const bar =
[{b:4},{b:5},{b:6}]
const result =
zip(foo, bar).map(o => Object.assign({}, ...o))
console.log(JSON.stringify(result))
[{"a":1,"b":4},{"a":2,"b":5},{"a":3,"b":6}]
many arrays, any size
Above, you will run into strange output if a or b is longer than the other. I think a better approach is to use generators though. It works for any number of input arrays of any size -
const iter = t =>
t?.[Symbol.iterator]()
function* zip (...its)
{ let r, g = its.map(iter)
while (true)
{ r = g.map(it => it.next())
if (r.some(v => v.done)) return
yield r.map(v => v.value)
}
}
const foo =
[{a:1},{a:2},{a:3}]
const bar =
[{b:4},{b:5},{b:6}]
const qux =
[{c:7},{c:8}]
const result =
Array.from(zip(foo, bar, qux), o => Object.assign({}, ...o))
console.log(JSON.stringify(result))
This does the zipping and transformation in a single pass, without the need map afterward -
[{"a":1,"b":4,"c":7},{"a":2,"b":5,"c":8}]
without generators
If you don't like generators but still want the flexibility offered by the solution above, we can write a simple zip2 -
const zip2 = ([a, ...nexta], [b, ...nextb]) =>
a == null || b == null
? [] // empty
: [ [a, b], ...zip2(nexta, nextb) ] // recur
And then the variadiac zip which accepts any amount of arrays of any size -
const zip = (t, ...more) =>
more.length
? zip2(t, zip(...more)).map(([a, b]) => [a, ...b]) // flatten
: t.map(a => [a]) // singleton
Now we can zip any amount of arrays -
const foo =
[{a:1},{a:2},{a:3}]
const bar =
[{b:4},{b:5},{b:6}]
const qux =
[{c:7},{c:8}]
const result =
zip(foo, bar, qux).map(o => Object.assign({}, ...o))
console.log(JSON.stringify(result))
Expand the snippet below to verify the result in your own browser -
const zip2 = ([a, ...nexta], [b, ...nextb]) =>
a == null || b == null
? []
: [ [a, b], ...zip2(nexta, nextb) ]
const zip = (t, ...more) =>
more.length
? Array.from(zip2(t, zip(...more)), ([a, b]) => [a, ...b])
: t.map(a => [a])
const foo =
[{a:1},{a:2},{a:3}]
const bar =
[{b:4},{b:5},{b:6}]
const qux =
[{c:7},{c:8}]
const result =
zip(foo, bar, qux).map(o => Object.assign({}, ...o))
console.log(JSON.stringify(result))
[{"a":1,"b":4,"c":7},{"a":2,"b":5,"c":8}]
You can try this too with map and reduce, this is just another alternative
function merge(...args) {
// finding highest length Array to not skip missing elements from other arrays
// for skipping missing elements use "acc.length < ele.length"
const maxArray = args.reduce((acc, ele) => acc.length > ele.length ? acc : ele);
//Iterating over highest length array
return maxArray.map((ele, index) =>
//merging all the instances in arrays with same index
args.reduce((acc, group) => Object.assign(acc, group[index]), {})
);
}
merge([ {k: 'v1'}, {k: 'v2'}, {k: 'v3'} ], [ {k2: 'v4'}, {k2: 'v5'}, {k2: 'v6'} ]);
// [{"k":"v1","k2":"v4"},{"k":"v2","k2":"v5"},{"k":"v3","k2":"v6"}]
merge([ {k: 'v1'}, {k: 'v2'}], [ {k2: 'v4'}, {k2: 'v5'}, {k2: 'v6'} ])
// [{"k":"v1","k2":"v4"},{"k":"v2","k2":"v5"},{"k2":"v6"}]
merge([ {k: 'v1'}, {k: 'v2'}, {k: 'v3'} ], [ {k2: 'v4'}, {k2: 'v5'}])
//[{"k":"v1","k2":"v4"},{"k":"v2","k2":"v5"},{"k":"v3"}]
Here's a fairly straightforward solution using .reduce() that will accept any number of arrays of various lengths.
const
foo = [{ a: 1 }, { a: 2 }, { a: 3 }],
bar = [{ b: 4 }, { b: 5 }, { b: 6 }],
qux = [{ c: 7 }, { c: 8 }],
zip = (...arrs) =>
arrs.reduce((a, arr) => {
arr.forEach((x, i) => Object.assign((a[i] = a[i] || {}), x));
// or using logical nullish assignment
// arr.forEach((x, i) => Object.assign((a[i] ??= {}), x));
return a;
}, []);
result = zip(foo, bar, qux);
console.log(JSON.stringify(result))
// [{ a: 1, b: 4, c: 7 }, { a: 2, b: 5, c: 8 }, { a: 3, b: 6 }]
I wanted to share what I ended up doing cause it worked well with both nested arrays and simple object arrays and is formatted for getting info straight from an await from mongo db, sadly its just a filter map tho.
blog obj is
{
title:"stuff",
p1:"stuff"
}
and the return is the zipped array.
const everyOtherBlog = Object.values(blogObj).map((val) => {
const b = blogs
.filter((f) => !JSON.stringify(f).includes(val))
.map(({ _doc }) => {
const keys = Object.keys(_doc).filter((k) =>
Object.keys(blogObj).includes(k)
);
const entryObj = Object.fromEntries(keys.map((k) => [k, _doc[k]]));
return entryObj;
});
return b[0];
});

Rename keys with ramda js

const orignalArr = [
{
personName: 'Joe'
}
]
expected output:
const convertedArr = [
{
name: 'Joe'
}
]
I'm thinking the renamed keys are defined in an object (but fine if there's a better way to map them):
const keymaps = {
personName: 'name'
};
How can I do this with Ramda?
Something with R.map
There is an entry in Ramda's Cookbook for this:
const renameKeys = R.curry((keysMap, obj) =>
R.reduce((acc, key) => R.assoc(keysMap[key] || key, obj[key], acc), {}, R.keys(obj))
);
const originalArr = [{personName: 'Joe'}]
console .log (
R.map (renameKeys ({personName: 'name'}), originalArr)
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
But with the ubiquity of ES6, it's pretty easy to write this directly:
const renameKeys = (keysMap) => (obj) => Object.entries(obj).reduce(
(a, [k, v]) => k in keysMap ? {...a, [keysMap[k]]: v} : {...a, [k]: v},
{}
)
You can combine Ramda with Ramda Adjunct. Using the renameKeys (https://char0n.github.io/ramda-adjunct/2.27.0/RA.html#.renameKeys) method is very useful. With it you can simply do something like this:
const people = [
{
personName: 'Joe'
}
]
const renameKeys = R.map(RA.renameKeys({ personName: 'name' }));
const __people__ = renameKeys(people);
console.log(__people__) // [ { name: 'Joe' }]
Hope it helped you :)
This is my take on renameKeys. The main idea is to separate the keys and values to two array. Map the array of keys, and replace with values from keyMap (if exist), then zip back to object:
const { pipe, toPairs, transpose, converge, zipObj, head, map, last } = R
const renameKeys = keysMap => pipe(
toPairs, // convert to entries
transpose, // convert to array of keys, and array of values
converge(zipObj, [ // zip back to object
pipe(head, map(key => keysMap[key] || key)), // rename the keys
last // get the values
])
)
const originalArr = [{ personName: 'Joe', lastName: 'greg' }]
const result = R.map(renameKeys({ personName: 'name' }), originalArr)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
My idea to make it is to first check that the old prop I want to rename exists, and the new key I want to create doesn’t.
Then, I will use the S_ common combinator to make it point-free.
Find JS common combinators here
const {
allPass, assoc, compose: B, complement, has, omit, prop, when
} = require('ramda');
const S_ = (f) => (g) => (x) => f (g (x)) (x);
const renameKey = (newKey) => (oldKey) => when(allPass([
has(oldKey)
, complement(has)(newKey)
]))
(B(omit([oldKey]), S_(assoc(newKey))(prop(oldKey))))
const obj = { fullname: 'Jon' };
renameKey('name')('fullname')(obj) // => { name: ‘Jon’ }
Here is my own solution, not too many arrow functions (just one), mostly pure Ramda calls. And it is one of shortest, if not the shortest ;)
First, based on your example
const { apply, compose, either, flip, identity, map, mergeAll, objOf, prop, replace, toPairs, useWith } = require('ramda');
const RenameKeys = f => compose(mergeAll, map(apply(useWith(objOf, [f]))), toPairs);
const originalArr = [
{
personName: 'Joe',
},
];
const keymaps = {
personName: 'name',
};
// const HowToRename = flip(prop)(keymaps); // if you don't have keys not specified in keymaps explicitly
const HowToRename = either(flip(prop)(keymaps), identity);
console.log(map(RenameKeys(HowToRename))(originalArr));
Second option, using any arbitrary lambda with renaming rules:
const { apply, compose, map, mergeAll, objOf, replace, toPairs, useWith } = require('ramda');
const RenameKeys = f => compose(mergeAll, map(apply(useWith(objOf, [f]))), toPairs);
const HowToRename = replace(/(?<=.)(?!$)/g, '_'); // for example
console.log(RenameKeys(HowToRename)({ one: 1, two: 2, three: 3 }));
Yields
{ o_n_e: 1, t_w_o: 2, t_h_r_e_e: 3 }
Third, you can use object-based rename rules from the first example and use fallback strategy, e.g. replace like in the second example, instead of identity.

Parsing and Convert Data type into Json in javascript

I have variable that contain array inside, when i was tried to print it with javascript console.log(res) show like this:
res = [{sakti: "23"},{Baim: "20"},{Jaka: "18"}]
How i suppose to do, if i want to change the data type into like this:
res = [{name: "sakti", y: 23},{name: "Baim", y: 20},{name: "Jaka", y: 18}]
my current code:
this.categoryservice.getRole().subscribe((res)=>{
console.log(res);
})
You can use map and Object.keys
let res = [{sakti: "23"},{Baim: "20"},{Jaka: "18"}]
let op = res.map(e=>{
let key = Object.keys(e)[0]
return { name: key, y: +e[key] }
})
console.log(op)
You can do this with Array.map, Object.entries and destructuring assignment:
const data = [{sakti: "23"}, {Baim: "20"}, {Jaka: "18"}];
const result = data.map(item => {
const [key, value] = Object.entries(item)[0];
return { name: key, y: value };
});
console.log(result);
Array.from is another way of mapping the object array into a new array of objects by using the second mapping argument. Object.keys & Object.values can be used to construct the new object by taking the [0] position from the key array which will be the name and [0] from the value array which will be the y key.
const res = [{sakti: "23"},{Baim: "20"},{Jaka: "18"}]
const arrayConv = Array.from(res, obj => { return {"name":Object.keys(obj)[0], "y":Object.values(obj)[0] } });
console.log(arrayConv);
you can use map and object.entries for this
var res = [{sakti: "23"},{Baim: "20"},{Jaka: "18"}]
var result = res.map((i)=>{
let obj = Object.entries(i);
return {'name': obj[0][0], 'y': obj[0][1]};
});
console.log(result);
With the new experimental flatMap() you can create a generic approach (in case one of your object have more than one key:val pair):
const res = [{sakti: "23", foo: "33"},{Baim: "20"},{Jaka: "18"}];
let mapped = res.flatMap(o => Object.entries(o).map(([k, v]) => ({name: k, y: +v})));
console.log(mapped);
But, you can always use reduce() for this too:
const res = [{sakti: "23", foo: "33"},{Baim: "20"},{Jaka: "18"}];
let mapped = res.reduce(
(acc, o) => acc.concat(Object.entries(o).map(([k, v]) => ({name: k, y: +v}))),
[]
);
console.log(mapped);

Get union of keys of all objects in js array using reduce

Assume , we have :
var all=[
{firstname:'Ahmed', age:12},
{firstname:'Saleh', children:5 }
{fullname: 'Xod BOD', children: 1}
];
The expected result is ['firstname','age', 'children', 'fullname']: the union of keys of all objects of that array:
all.map((e) => Object.keys(e) ).reduce((a,b)=>[...a,...b],[]);
This is work fine , However, i am seeking a solution more performance using directly reduce method without map , I did the following and it is failed.
all.reduce((a,b) =>Object.assign([...Object.keys(a),...Object.keys(b)]),[])
You can use Set, reduce() and Object.keys() there is no need for map.
var all=[
{firstname:'Ahmed', age:12},
{firstname:'Saleh', children:5 },
{fullname: 'Xod BOD', children: 1}
];
var result = [...new Set(all.reduce((r, e) => [...r, ...Object.keys(e)], []))];
console.log(result)
Here's a solution using generic procedures concat, flatMap, and the ES6 Set.
It's similar to #NenadVracar's solution but uses higher-order functions instead of a complex, do-it-all-in-one-line implementation. This reduces complexity in your transformation and makes it easier to re-use procedures in other areas of your program.
Not that ... spread syntax is bad, but you'll also notice this solution does not necessitate it.
var all = [
{firstname:'Ahmed', age:12},
{firstname:'Saleh', children:5 },
{fullname: 'Xod BOD', children: 1}
];
const concat = (x,y) => x.concat(y);
const flatMap = f => xs => xs.map(f).reduce(concat, []);
const unionKeys = xs =>
Array.from(new Set(flatMap (Object.keys) (xs)));
console.log(unionKeys(all));
// [ 'firstname', 'age', 'children', 'fullname' ]
Just out of curiosity, I've been benchmarking some solutions to your problem using different approaches (reduce vs foreach vs set). Looks like Set behaves well for small arrays but it's extremely slow for bigger arrays (being the best solution the foreach one).
Hope it helps.
var all = [{
firstname: 'Ahmed',
age: 12
}, {
firstname: 'Saleh',
children: 5
}, {
fullname: 'Xod BOD',
children: 1
}],
result,
res = {};
const concat = (x,y) => x.concat(y);
const flatMap = f => xs => xs.map(f).reduce(concat, []);
const unionKeys = xs =>
Array.from(new Set(flatMap (Object.keys) (xs)));
for(var i = 0; i < 10; i++)
all = all.concat(all);
console.time("Reduce");
result = Object.keys(all.reduce((memo, obj) => Object.assign(memo, obj), {}));
console.timeEnd("Reduce");
console.time("foreach");
all.forEach(obj => Object.assign(res, obj));
result = Object.keys(res);
console.timeEnd("foreach");
console.time("Set");
result = [...new Set(all.reduce((r, e) => r.concat(Object.keys(e)), []))];
console.timeEnd("Set");
console.time("Set2");
result = unionKeys(all);
console.timeEnd("Set2");
Try this code:
var union = new Set(getKeys(all));
console.log(union);
// if you need it to be array
console.log(Array.from(union));
//returns the keys of the objects inside the collection
function getKeys(collection) {
return collection.reduce(
function(union, current) {
if(!(union instanceof Array)) {
union = Object.keys(union);
}
return union.concat(Object.keys(current));
});
}

Categories