How to get values between two numbers in an array? - javascript

["10","13"] is the array, need to find the values between them and flatten the array with the values -> ["10","11","12","13"].
The first array above (["10", "13"]) is in a list of arrays.
const OriginalData = {
Red:{
Name:"L",
List:[
["1", "5"],
["2", "5"],
["7", "9" ],
]
},
Blue:{
Name:"BL",
List:[
["1", "5"],
["7", "9" ],
["10", "13" ],
["15", "20"]
]
},
Black:{
List:[
["Random"],
"Random2"
]
}
}
Then finally Object must look like,
{
Level:{
Name:"L",
List:[
1, 2, 3, 4, 5, 7, 8, 9
]
},
Basement:{
Name:"BL",
List:[
1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20
]
},
Custom:{
List:[
"Random",
"Random2"
]
}
}
What It should do:
Take the first object, inside List there are set of ranges, the values between those ranges should be found a flatten without duplicates.
Finding the values between is only for "Red", and "Blue", In "Black" key only flatten is needed.
I tried,
Code:
const submitData = () => {
let obj = originalData;
let flattenedArray = [].concat.apply([], originalData.Red.List);
let uniqueArray = flattenedArray.filter(
(v, i, a) => a.indexOf(v) === i
);
obj = {
...originalData,
Red: {
...originalData.Red,
List: uniqueArray,
},
};
console.log(obj);
};
The above code flattens the array but will not find between the numbers and it only worked for key "Red"

A simple example to create a range:
let example = ["10","13"];
let min = Math.min(...example);
let max = Math.max(...example);
let result = [];
for (i = min; i <= max; i++) {
result.push(i);
}
console.log(min, max, result)

You can easily achieve it with a simple logic and will work for random numbers as well.
Try this (Descriptive comments of implementation has been added in the below code snippet) :
const OriginalData = {
Red:{
Name:"L",
List:[
["1", "5"],
["2", "5"],
["7", "9" ],
]
},
Blue:{
Name:"BL",
List:[
["1", "5"],
["7", "9" ],
["10", "13" ],
["15", "20"]
]
}
};
Object.keys(OriginalData).forEach(key => {
// Flatten the original array list.
OriginalData[key].List = OriginalData[key].List.flat()
// Find min and max numbers from the array.
const min = Math.min(...OriginalData[key].List);
const max = Math.max(...OriginalData[key].List);
// empty existing list array.
OriginalData[key].List = [];
// Now using for loop assign the values to the list array based on min and max value.
for (let i = min; i <= max; i++) {
OriginalData[key].List.push(i);
}
});
// Result
console.log(OriginalData);

To create an array of all the numbers in a given range, you can create a new array with the required size and then map each of it's entries to the entry's index plus the given lower bound.
function fillRange(r) {
let b = +r[1]
let a = +r[0]
if (a > b) {
let tmp = a
a = b
b = tmp
}
return Array(b - a + 1).fill(0).map((e, i) => a + i)
}
This function flattens an array and removes all duplicate entries.
function union(arrays) {
let flattened = [].concat.apply([], arrays)
return flattened.reduce(
(total, e) => {
let i = total.indexOf(e)
if (i === -1) {
total.push(e)
}
return total
}, [])
}
Then this code produces the desired result from a list of ranges:
function unionRanges(ranges) {
let expanded = ranges.map((e) => fillRange(e))
return union(expanded).sort((a,b) => (a-b))
}
The final object can be created like this:
function processData(data) {
let res = {}
res.Level = {}
res.Basement = {}
res.Custom = {}
res.Level.Name = data.Red.Name;
res.Level.List = unionRanges(data.Red.List)
res.Basement.Name = data.Blue.Name
res.Basement.List = unionRanges(data.Blue.List)
res.Custom.List = union(data.Black.List)
return res
}

You can flat the array, get the min and max and finally with a loop create the desired array.
const array = [["1","5"],["7","9"],["10","13"],["15","20"]];
const flatted = array.flat();
const min = Math.min(...flatted);
const max = Math.max(...flatted);
const result = Array.from({length: max + 1 - min}).map(function(_, i) {
return i + min;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Hope the below codes help you.
const OriginalData = {
Red: {
Name: "L",
List: [
["1", "5"],
["2", "5"],
["7", "9"],
]
},
Blue: {
Name: "BL",
List: [
["1", "5"],
["7", "9"],
["10", "13"],
["15", "20"]
]
},
Black: {
List: [
["Random"],
"Random2"
]
}
};
//Iterating over the object OriginalData
Object.keys(OriginalData).forEach(key => {
// Flatten the array "List" of each element
OriginalData[key].List = OriginalData[key].List.flat();
// Checking if the flatten array contains integers
if (!isNaN(parseInt(OriginalData[key].List[0]))) {
// Calculating the min and max of the "List" array
const min = Math.min(...OriginalData[key].List);
const max = Math.max(...OriginalData[key].List);
let tmpArr = [];
// Generating the array with numbers from min to max
for (let i = min; i <= max; i++) {
tmpArr.push(i);
}
// Updating the original "List" (containing integers) of each element
OriginalData[key].List = tmpArr;
}
});
console.log(OriginalData);

Related

Fastest way to loop over 2 mixed arrays and return the matching value on intersection

I'm trying to create a JavaScript method which loops over 2 arrays and returns an array of the matched value.
My a1 parameter in the 'getMatchedArray' method is an array of strings and objects, while arr2 is always array of objects.
However, a2 parameter in the 'getMatchedArray' method is an array that can contain an object with value property or without value property as seen in the sample arrays used.
I'm very close to it but somehow not able to figure out, what is the mistake I'm making?
Is there a faster way using intersection to achieve this?
const arr1 = ["red", {
"code": "red",
"label": "test"
}, {
"code": "blue",
"label": "test1"
}, "white", "blue", {
"code": "red",
"label": "test2"
}];
const arr2 = [{
"code": "red",
"value": "test2"
}];
const arr3 = [{
"code": "blue"
}];
const arr4 = [{
"code": "red",
"value": "test3"
}]
function getMatchedArray(a1, a2) {
return a1.reduce((memo, opt) => {
const isOptionFound = a2.some(obj => {
if (obj.value) {
return obj.value === opt.label;
} else {
return !opt.code && opt === obj.code;
}
});
if (isOptionFound) {
memo.push(opt);
}
return memo;
}, []);
}
const result1 = getMatchedArray(arr1, arr2);
const result2 = getMatchedArray(arr1, arr3);
const result3 = getMatchedArray(arr1, arr4);
console.log(result1);
console.log(result2);
console.log(result3);
Expected output:
result1:
[{
"code": "red",
"label": "test2"
}]
result2: ["blue"]
result3: ["red"]
result1, result 2 are fine, but my result3 is incorrect.
Any help on this?
//Try this
function findMatchingValues(arr1, arr2) {
const hashTable = {};
const matchingValues = [];
// Populate hash table with values from arr1
for (let i = 0; i < arr1.length; i++) {
const val = arr1[i];
hashTable[val] = true;
}
// Check arr2 for matching values
for (let i = 0; i < arr2.length; i++) {
const val = arr2[i];
if (hashTable[val]) {
matchingValues.push(val);
}
}
return matchingValues;
}
You can also achieve this requirement by separating the string and object elements from an array and then applied filter on those arrays based on the passed 2nd parameter in the function by stringify the passed parameters in the function.
Live Demo :
const arr1 = ["red", {
"code": "red",
"label": "test"
}, {
"code": "blue",
"label": "test1"
}, "white", "blue", {
"code": "red",
"label": "test2"
}];
const arr2 = [{
"code": "red",
"value": "test2"
}];
const arr3 = [{
"code": "blue"
}];
const arr4 = [{
"code": "red",
"value": "test3"
}];
function getMatchedArray(a1, a2) {
let strA2 = JSON.stringify(a2);
const strArrayFromA1 = a1.filter(item => typeof item === 'string');
const objArrayFromA1 = a1.filter(item => typeof item === 'object');
const matchedObject = objArrayFromA1.filter(elem => {
strA2 = strA2.replaceAll('value', 'label');
return strA2.includes(JSON.stringify(elem));
});
const matchedString = strArrayFromA1.filter(elem => strA2.includes(elem));
return matchedObject.length ? matchedObject : matchedString.length ? matchedString : 'No match found.';
}
const result1 = getMatchedArray(arr1, arr2);
const result2 = getMatchedArray(arr1, arr3);
const result3 = getMatchedArray(arr1, arr4);
console.log(result1);
console.log(result2);
console.log(result3);
The issue with the third result is that the expected output is an array of objects with the matching values, but the current implementation is returning a single object. To fix this, you can modify the function to push the opt value to memo instead of the opt object when there is a match in the arr4.
Here is the modified function:
function getMatchedArray(a1, a2) {
return a1.reduce((memo, opt) => {
const isOptionFound = a2.some(obj => {
if (obj.value) {
return obj.value === opt.label;
} else {
return !opt.code && opt === obj.code;
}
});
if (isOptionFound) {
memo.push(opt.label || opt);
}
return memo;
}, []);
}
With this modification, the output for result3 will be ["red"], which is the expected result.
Regarding the second part of the question, there is a faster way to achieve this using the filter and includes array methods. Here is an example implementation:
function getMatchedArray(a1, a2) {
return a1.filter(opt => {
return a2.some(obj => {
if (obj.value) {
return obj.value === opt.label;
} else {
return opt.code && obj.code && opt.code === obj.code;
}
});
});
}
This implementation uses filter to create a new array with all the elements that match the condition, and includes to check if the a2 array includes the opt value.

Unnest object with array values

Here's my input object —
{
"email": [
"abc"
],
"name": [
"def",
"ghi"
],
"number": [
"123",
"456"
]
}
Here's what I'm hoping to get as output —
[
{
"email":"abc",
"name":"def",
"number":"123"
},
{
"email":"abc",
"name":"ghi",
"number":"123"
},
{
"email":"abc",
"name":"def",
"number":"456"
},
{
"email":"abc",
"name":"ghi",
"number":"456"
}
]
And, here's my code —
const input = {
"email": [
"abc"
],
"name": [
"def",
"ghi"
],
"number": [
"123",
"456"
]
};
const keys = Object.keys(input);
const values = Object.values(input);
let depth = [];
let output = [];
values.forEach(value => depth.push(value.length));
depth = depth.reduce((a, b)=> a*b, 1);
let dict = {};
for (let i = 0; i < depth; i++) {
for (let j = 0; j < keys.length; j++) {
let key = keys[j];
if (input[key][i] !== undefined) {
dict[key] = input[key][i];
}
}
console.log(dict);
output.push(dict);
}
console.log(output);
Your approach by calculating the product of the lengths is certainly one that can work. But there are some problems with the implementation that make it fail:
With let dict = {}; you only create one object, and that object is being pushed to the result array repeatedly. Any mutation to that object will thus be seen in very entry of that result array. So you should at least create that dict object in every iteration of the outer loop
i will in many cases exceed the length of the input[key] array, so input[key][i] will be undefined. Yet you need to pick a value from that array. You should use modular logic to translate such i to a valid index, and then use the remainder of that i in the next iteration of the inner loop -- to pick a value from the next array.
Here is a slight adaptation of your code to tackle those issues. I also moved it into a function:
function cartesian(input) {
let keys = Object.keys(input);
let depth = Object.values(input).reduce((product, {length}) => product * length, 1);
let result = [];
for (let i = 0; i < depth; i++) {
let j = i;
let dict = {};
for (let key of keys) {
let size = input[key].length;
dict[key] = input[key][j % size];
j = Math.floor(j / size);
}
result.push(dict);
}
return result;
}
const input = {
"email": [
"abc"
],
"name": [
"def",
"ghi"
],
"number": [
"123",
"456"
]
};
let result = cartesian(input);
console.log(result);
This answer is based on the answers provided on this question.
Step 1
Extract the three arrays:
const {email, name, number} = input;
Step 2
Perform the cartesian product:
const cartesian = (...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [d, e].flat())));
const product = cartesian(email, name, number);
Step 3
Recompose the output:
const output = product.map(triple => ({email: triple[0], name: triple[1], number: triple[2]}));
You can replace the cartesian function with other functions found in the related question, accordingly to the target version of ES you mean to support.
#pilchard proposes a more generic version, which doesn't need you to specify the properties, but just performs the cartesian product on all the ones available within an object, and it is:
const result = cartesian(...Object.entries(input).map(([k, vs]) => vs.map(v => [[k, v]]))).map(Object.fromEntries)

Multiple JS object merged with keys in one

I need to transform 3 js objects in one object.
I have this vars with same length:
var date = ["2020-10-09", "2020-10-15", ...]
var count= [2, 5, ...]
var name= ["Ivan", "Maria", ...]
I want to transform the three vars in this structure:
var alldata = [
{
"date": "2020-10-09",
"count": 2,
"name": "Ivan"
},
{
"date": "2020-10-15",
"count": 5,
"name": "Maria"
}]
try this
var count= [2, 5]
var name= ["Ivan", "Maria"]
let employee = [];
for (let i = 0; i < date.length; i++) {
employee.push(
{
date:date[i],
count:count[i],
name:name[i]
}
);
}
Try this code:
var date = ['2020-10-09', '2020-10-15'];
var count = [2, 5];
var name = ['Ivan', 'Maria'];
const alldata = Array.from({
length: Math.max(date.length, count.length, name.length),
}).map((_, i) => ({ date: date[i], name: name[i], count: count[i] }));
console.warn(alldata)
or in you case that you know they are the same length
var date = ['2020-10-09', '2020-10-15'];
var count = [2, 5];
var name = ['Ivan', 'Maria'];
const alldata = date.map((_, i) => ({
date: date[i],
name: name[i],
count: count[i],
}));
console.warn(alldata);
You can "zip" the two arrays, and for each zipped array, you can use Object.fromEntries() on the zipped arrays. Object.fromEntries() takes an array of key-value pairs, which you can produce by mapping each array to an array of key [key, value] pairs like so:
const date = ["2020-10-09", "2020-10-15"];
const count = [2, 5];
const name = ["Ivan", "Maria"];
const arrs = [date, count, name];
const keys = ["date", "count", "name"]; // key name for each array
const res = arrs[0].map(
(_, i) => Object.fromEntries(arrs.map((r,j)=>[keys[j], r[i]])
));
console.log(res);

JS -Append key to matching array, object values

I would like to push key values to objects in array1 from other objects of array2
To do so it needs to search a corresponding values in both arrays, then push the right key.
let array1 = [
{
"Ref": "28189-060-B",
"Otherkey": "Whatever"
},
{
"Ref": "18182-250-B",
"Otherkey": "Whatever2"
},
{
"Ref": "55187-753-B",
"Otherkey": "Whatever3"
}
]
let array2 = [
{
"Ref": "28189-060-ABCD",
"Style": "Red"
},
{
"Ref": "18182-250-ABCD",
"Style": "Blue"
},
{
"Ref": "55187-753-ABCD",
"Style": "Yellow"
}
]
The function need to loop through all objects in array1, look at the first 9 characters of Ref values, find a match in array2 Ref (only first 9 characters are identical). When there is a match push the "Style" from array2 into the corresponding object in array1
I tried with Object.key.foreach(), map(), with substr to get only 9 characters, with find()... all of this has been a big mess and not working...
Expected result :
let array1 = [
{
"Ref": "18182-250-B",
"Otherkey": "Whatever2",
"Style": "Blue"
},
{
"Ref": "28189-060-B",
"Otherkey": "Whatever",
"Style": "Red"
},
{
"Ref": "55187-753-B",
"Otherkey": "Whatever3",
"Style": "Yellow"
}
]
Assuming those properties are all meant to be Ref (some are Global_Style), you can use forEach and find:
let array1 = [{"Ref":"28189-060-B","Otherkey":"Whatever"},{"Ref":"18182-250-B","Otherkey":"Whatever2"},{"Ref":"55187-753-B","Otherkey":"Whatever3"}];
let array2 = [{"Ref":"28189-060-ABCD","Style":"Red"},{"Ref":"18182-250-ABCD","Style":"Blue"},{"Ref":"55187-753-ABCD","Style":"Yellow"}];
const shorterRef = (ref) => ref.substr(0, 9);
array1.forEach(obj => {
const a1Ref = shorterRef(obj.Ref);
const arr2Obj = array2.find(tmp => shorterRef(tmp.Ref) === a1Ref);
if (arr2Obj) obj.Style = arr2Obj.Style;
});
console.log(array1);
If you didn't want to mutate the array go with map:
let array1 = [{"Ref":"28189-060-B","Otherkey":"Whatever"},{"Ref":"18182-250-B","Otherkey":"Whatever2"},{"Ref":"55187-753-B","Otherkey":"Whatever3"}];
let array2 = [{"Ref":"28189-060-ABCD","Style":"Red"},{"Ref":"18182-250-ABCD","Style":"Blue"},{"Ref":"55187-753-ABCD","Style":"Yellow"}];
const shorterRef = (ref) => ref.substr(0, 9);
const out = array1.map(obj => {
const a1Ref = shorterRef(obj.Ref);
const arr2Obj = array2.find(tmp => shorterRef(tmp.Ref) === a1Ref);
if (arr2Obj) return { ...obj, Style: arr2Obj.Style };
});
console.log(out);
var arrMap = {};
array1.forEach(function(x){
if(!arrMap[x.Ref.substring(0,9)]){
arrMap[x.Ref.substring(0,9)] = x;
}
});
array2.forEach(function(x){
if(Object.keys(arrMap).includes(x.Ref.substring(0,9))){
arrMap[x.Ref.substring(0,9)] = Object.assign(arrMap[x.Ref.substring(0,9)], {"Style": x.Style});
}
});
console.log(Object.values(arrMap));
Something like this may be what you want:
array1.forEach(function (element1) {
array2.forEach(function (element2){
addStyle(element1, element2);
});
});
function addStyle(obj1, obj2){
if (obj1.Ref && obj2.Ref){
let Ref1 = obj1.Ref.substr(0,8);
let Ref2 = obj2.Ref.substr(0, 8);
if (Ref1 === Ref2){
obj1.Style = obj2.Style;
};
}
}
So we loop through the fist array and for each item we loop through the second array.
Then we check if the expected fields are present and if so we compare them. If they match we add the "Style" field and move to the next object
The Below code will work although we might be able to optimize it further.
var newArr = []
for(let k in array1){
for(let i in array2){
console.log(array2[i]['Ref'].substr(0,9))
if(array1[k]['Ref'].substr(0,9) == array2[i]['Ref'].substr(0,9)){
let temp = array1[k]
temp['Style'] = array2[i]['Style']
newArr.push(temp)
}
}
}
The first solution is a bit complex.
You probable have a typo in array1 as your first key is not consistent. instead of Global_Stylecode you probably meant Ref, Anyway most likely it should have the same key. If we assume that the key is Ref, then
array1.forEach( ({Ref: Ref1, Otherkey}, index) => {
const Ref1Sub = Ref1.substring(0, 9);
array2.forEach(({Ref: Ref2, Style}) => {
if (Ref2.includes(Ref1Sub)) {
array1[index].Style = Style;
}
})
});
Also there is no need to define arrays as let. const will be fine.

Get the object with the lowest value attribute from array

I know I can get the object with the lowest value attribute by executing
const obj = Math.min.apply(Math, myArr.map(o => { return o.val; }));
but I have to return the object, not the value of the object. How can I return the object from this?
My current way is
const lowest = (arr.sort((a, b) => a.val < b.val))[0];
but maybe there is a more optimized way.
Working example:
function Obj(val) {
this.val = val;
}
$(document).ready(() => {
const data = [new Obj(2), new Obj(7), new Obj(9), new Obj(1), new Obj(3)];
const lowestNumber = Math.min.apply(Math, data.map(o => {
return o.val;
}));
console.log(lowestNumber);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Not working:
function Obj(val) {
this.val = val;
}
$(document).ready(() => {
const data = [new Obj(2), new Obj(7), new Obj(9), new Obj(1), new Obj(3)];
const obj = Math.min.apply(Math, data.map(o => {
return o;
}));
console.log(obj);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
This returns NaN
Since you want the object, and not the value, you cannot use Math.min.
For linear algo, use reduce instead:
const lowest = arr.reduce( (acc, a) => a.val < acc.val ? a : acc, arr[0]);
This might work:
const lowest = data.reduce((a, b) => a.val < b.val ? a : b);
The reduce function is used to "reduce" an array to a single value, based on comparisons between consecutive values. An extended explanation:
const lowest = data.reduce(
function(a, b) {
if (a.val < b.val) {
// keep a as next value
return a
} else {
// keep b as next value
return b
}
})
If you are not well experienced with some special Math/Array functions, such easy task can be just implemented directly by you.
Sorting is not good way, if you have big arrays and you need reasonable performance.
const arr = [
{ val: 3 },
{ val: 1 }
]
let minObj = arr[0];
arr.forEach(obj => {
if (obj.val < minObj.val) {
minObj = obj;
}
});
console.log(minObj);
This will also do it.. I believe this method has good performance too
var arr = [
{"ID": "test1", "val": 1},
{"ID": "test2", "val": 2},
{"ID": "test3", "val": 3},
{"ID": "test4", "val": 4}
]
arr.reduce(function(p, c) {
return p.val < c.val ? p : c;
});

Categories