How to get data from multidimensional array in JavaScript? - javascript

I have the following array:
var data = [{
length: 900,
fields: 3
},{
length: 1150,
fields: 4
},{
length: 1700,
fields: 5
}];
Now I would like to have a function that returns the fields depending on the given length like:
function getFields(length) {
// return "3" if length <= 900
// return "4" if length <= 1150
// return "5" if length <= 1700
}
How could I achieve this?

As long as data is properly sorted, it is a simple for loop
var data = [{
length: 900,
fields: 3
},{
length: 1150,
fields: 4
},{
length: 1700,
fields: 5
}];
function getFields (value) {
var i;
for (i=0; i<data.length; i++) {
if (value <= data[i].length) return data[i].fields; // exit since we found first match
}
return 0; // what ever the default is for no match
}
console.log(800, getFields(800));
console.log(900, getFields(900));
console.log(1000, getFields(1000));
console.log(1500, getFields(1500));
console.log(2000, getFields(2000));
or with modern array methods you can use find() which is like a for loop code above under the hood:
var data = [{
length: 900,
fields: 3
},{
length: 1150,
fields: 4
},{
length: 1700,
fields: 5
}];
function getFields (value) {
var i;
var match = data.find(function(item) {
return value <= item.length
})
return match ? match.fields : 0;
}
console.log(800, getFields(800));
console.log(900, getFields(900));
console.log(1000, getFields(1000));
console.log(1500, getFields(1500));
console.log(2000, getFields(2000));
Now if the data array is out of order, than it should be sorted.

I'd define it like so:
function getFields(length) {
var d = data
.filter(d => d.length <= length) // get the list of matching objects
.sort((a, b) => b.length - a.length) // sort descending so largest value is at the front of the array
.shift(); // get the first element from the array
return (d !== undefined) ? d.fields : undefined;// if that element exists, return .fields, otherwise undefined
}
In action:
var data = [{
length: 900,
fields: 3
},{
length: 1150,
fields: 4
},{
length: 1700,
fields: 5
}];
function getFields(length) {
var d = data
.filter(d => d.length <= length) // get the list of matching objects
.sort((a, b) => b.length - a.length) // sort descending so largest value is at the front of the array
.shift(); // get the first element from the array
return (d !== undefined) ? d.fields : undefined;// if that element exists, return .fields, otherwise undefined
}
var tests = [1700, 1150, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700];
console.log(tests.map(getFields));
While I don't know if this is performant enough for your current use case, but it's relatively readable and easy-to-follow (although this could be made more efficient if the data were always ordered by length, for instance). If you need something more performant, you could do something like this instead:
function getFields(length) {
let d;
let i = data.length - 1;
while (i > -1 && d === undefined) {
if (data[i].length <= length) {
d = data[i].fields;
}
i -= 1;
}
return d;
}
In action:
var data = [{
length: 900,
fields: 3
},{
length: 1150,
fields: 4
},{
length: 1700,
fields: 5
}];
function getFields(length) {
let d;
let i = data.length - 1;
while (i > -1 && d === undefined) {
if (data[i].length <= length) {
d = data[i].fields;
}
i -= 1;
}
return d;
}
var tests = [1700, 1150, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700];
console.log(tests.map(getFields));

you can iterate the data and match the conditon
var data = [{
length: 900,
fields: 3
},{
length: 1150,
fields: 4
},{
length: 1700,
fields: 5
}];
function getFields(len) {
var fields = '';
$.each(data, function(key,value) {
if(value.length<=len)
fields = value.fields;
});
return fields;
}
// call function
alert(getFields(1700));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

You don't need jQuery for this, it can be done with e.g. a standard .find() call. Note that this assumes that the data is sorted by .length as in your example.
var data = [{
length: 900,
fields: 3
}, {
length: 1150,
fields: 4
}, {
length: 1700,
fields: 5
}];
var value = 950;
var matching = data.find(x => value <= x.length);
var fields = matching ? matching.fields : 0;
console.log(fields);

Related

Java Script Random Number Generator Issue

I have the code to generate a random number and it seemes to be cycling back and forth between 1 or 2.
const isDnaUnique = (_DnaList = [], _dna = []) => { let foundDna =
_DnaList.find((i) => i.join("") === _dna.join("")); return foundDna == undefined ? true : false; };
const createDna = (_races, _race) => { let randNum = [];
_races[_race].layers.forEach((layer) => {
let randElementNum = Math.floor(Math.random() * 100) + 1;
let num = 0;
layer.elements.forEach((element) => {
if (randElementNum >= 100 - element.weight) {
num = element.id;
}
});
randNum.push(num); }); return randNum; };
My issue is the random number generator keeps only returning to values instead of cycling through all of them.
{
name: "Eyes",
elements: [
{
id: 0,
name: "E1",
path: `${dir}/3-eyes/E1.png`,
weight: 25,
},
{
id: 1,
name: "E2",
path: `${dir}/3-eyes/E2.png`,
weight: 25,
},
{
id: 2,
name: "E3",
path: `${dir}/3-eyes/E3.png`,
weight: 25,
},
{
id: 3,
name: "E4",
path: `${dir}/3-eyes/E4.png`,
weight: 25,
},
],
position: { x: 0, y: 0 },
size: { width: width, height: height },
},
Your results are exactly what I would expect. Let's take a look.
Your randElementNum is going to be a number from 1 to 100. All four of your elements have weight of 25. You are running through the loop for all of the elements every time. So, if the number is less than 75 (100-25), then the if statement never fires, and num will be 0. If the number is greater than or equal to 75, then the if statement fires all four times, and you'll end up with element #3. There are no other possibilities.
The next big problem is that "forEach" is the wrong tool. I've shown you how to make it work below, but you really should be using an old-fashioned "for" loop, so you can break the loop once you find an answer.
I'm not sure what effect you were trying for, but this is certainly not what you intended. Based on the name weight, were you trying to have each element get picked 25% of the time? You can do that with something like this:
const createDna = () => {
let randElementNum = Math.floor(Math.random() * 100);
console.log( randElementNum );
let num = -1;
layer.elements.forEach((element) => {
if( num >= 0 )
return;
if (randElementNum < element.weight)
{
num = element.id;
return;
}
randElementNum -= element.weight;
});
return num;
};

How to convert an unorganized array into an grouped array by id

I'm trying to create an array that contains objects with an id and amount, grouped by id. The ids needs to be unique. So if there is 2 objects with same id, the amount will be added.
I can do it with nested for-loops, but I find this solution inelegant and huge. Is there a more efficient or cleaner way of doing it?
var bigArray = [];
// big Array has is the source, it has all the objects
// let's give it 4 sample objects
var object1 = {
id: 1,
amount: 50
}
var object2 = {
id: 2,
amount: 50
}
var object3 = {
id: 1,
amount: 150
}
var object4 = {
id: 2,
amount:100
}
bigArray.push(object1,object2,object3,object4);
// organizedArray is the array that has unique ids with added sum. this is what I'm trying to get
var organizedArray = [];
organizedArray.push(object1);
for(var i = 1; i < bigArray.length; i++ ) {
// a boolean to keep track whether the object was added
var added = false;
for (var j = 0; j < organizedArray.length; j++){
if (organizedArray[j].id === bigArray[i].id) {
organizedArray[j].amount += bigArray[i].amount;
added = true;
}
}
if (!added){
// it has object with new id, push it to the array
organizedArray.push(bigArray[i]);
}
}
console.log(organizedArray);
You can definitly make it cleaner and shorter by using reduce, not sure about efficiency though, i would say a traditional for loop is more efficient :
var bigArray = [];
var object1 = {id: 1, amount: 50}
var object2 = {id: 2, amount: 50}
var object3 = {id: 1, amount: 150}
var object4 = {id: 2, amount: 100}
bigArray.push(object1, object2, object3, object4);
var organizedArray = bigArray.reduce((acc, curr) => {
// check if the object is in the accumulator
const ndx = acc.findIndex(e => e.id === curr.id);
if(ndx > -1) // add the amount if it exists
acc[ndx].amount += curr.amount;
else // push the object to the array if doesn't
acc.push(curr);
return acc;
}, []);
console.log(organizedArray)
Rather than an organized array, how about a single object whose keys are the ids and values are the sums.
var bigArray = [
{ id: 1, amount: 50 },
{ id: 2, amount: 50 },
{ id: 1, amount: 150 },
{ id: 2, amount: 100 }
];
let total = {}
bigArray.forEach(obj => {
total[obj.id] = (total[obj.id] || 0) + obj.amount;
});
console.log(total);
If you really need to convert this to an array of objects then you can map the keys to objects of your choosing like this:
var bigArray = [
{ id: 1, amount: 50 },
{ id: 2, amount: 50 },
{ id: 1, amount: 150 },
{ id: 2, amount: 100 }
];
let total = {}
bigArray.forEach(obj => {
total[obj.id] = (total[obj.id] || 0) + obj.amount;
});
console.log(total);
// If you need the organized array:
let organizedArray = Object.keys(total).map(key => ({ id: key, amount: total[key] }));
console.log(organizedArray);
function getUniqueSums(array) {
const uniqueElements = [];
const arrayLength = array.length;
for(let index = 0; index < arrayLength; index++) {
const element = array[index];
const id = element.id;
const uniqueElement = findElementByPropertyValue(uniqueElements, 'id', id);
if (uniqueElement !== null) {
uniqueElement.amount += element.amount;
continue;
}
uniqueElements.push(element);
}
return uniqueElements;
}
function findElementByPropertyValue(array, property, expectedValue) {
const arrayLength = array.length;
for(let index = 0; index < arrayLength; index++) {
const element = array[index];
const value = element[property];
if (value !== expectedValue) {
continue;
}
return element;
}
return null;
}
This is an untested code. You will be able to understand the logic. Logic is almost same yours. But, perhaps a more readable code.

how to count duplicate values object to be a value of object

how to count the value of object in new object values
lets say that i have json like this :
let data = [{
no: 3,
name: 'drink'
},
{
no: 90,
name: 'eat'
},
{
no: 20,
name: 'swim'
}
];
if i have the user pick no in arrays : [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
so the output should be an array
[
{
num: 3,
total: 11
},
{
num: 90,
total: 1
},
{
num:20,
total: 4
}
];
I would like to know how to do this with a for/of loop
Here is the code I've attempted:
let obj = [];
for (i of arr){
for (j of data){
let innerObj={};
innerObj.num = i
obj.push(innerObj)
}
}
const data = [{"no":3,"name":"drink"},{"no":90,"name":"eat"},{"no":20,"name":"swim"}];
const arr = [3,3,3,3,3,3,3,3,3,3,3,20,20,20,20,80,80];
const lookup = {};
// Loop over the duplicate array and create an
// object that contains the totals
for (let el of arr) {
// If the key doesn't exist set it to zero,
// otherwise add 1 to it
lookup[el] = (lookup[el] || 0) + 1;
}
const out = [];
// Then loop over the data updating the objects
// with the totals found in the lookup object
for (let obj of data) {
lookup[obj.no] && out.push({
no: obj.no,
total: lookup[obj.no]
});
}
document.querySelector('#lookup').textContent = JSON.stringify(lookup, null, 2);
document.querySelector('#out').textContent = JSON.stringify(out, null, 2);
<h3>Lookup output</h3>
<pre id="lookup"></pre>
<h3>Main output</h3>
<pre id="out"></pre>
Perhaps something like this? You can map the existing data array and attach filtered array counts to each array object.
let data = [
{
no: 3,
name: 'drink'
},
{
no:90,
name: 'eat'
},
{
no:20,
name: 'swim'
}
]
const test = [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
const result = data.map((item) => {
return {
num: item.no,
total: test.filter(i => i === item.no).length // filters number array and then checks length
}
})
You can check next approach using a single for/of loop. But first I have to create a Set with valid ids, so I can discard noise data from the test array:
const data = [
{no: 3, name: 'drink'},
{no: 90, name: 'eat'},
{no: 20, name: 'swim'}
];
const userArr = [3,3,3,3,3,3,3,3,7,7,9,9,3,3,3,90,20,20,20,20];
let ids = new Set(data.map(x => x.no));
let newArr = [];
for (i of userArr)
{
let found = newArr.findIndex(x => x.num === i)
if (found >= 0)
newArr[found].total += 1;
else
ids.has(i) && newArr.push({num: i, total: 1});
}
console.log(newArr);

Chaining multiple filters if condition exist

I'm trying to chain two filters, based in two ranges (arrays) of params that may also be empty, so it would be possible that f.ex. speedlimit=[]
var speedfilter =[240,300]
var pricefilter = [80,120]
var cars = [
{name:'Ferrari', maxspeed:240, price: 100},
{name:'Porsche', maxspeed:220, price: 90},
{name:'Bugatti', maxspeed:300, price: 500}
];
if (speedfilters) {
return cars.filter(function (car) {
return car.maxspeed >= speedfilter[0] && car.maxspeed <= speedfilter[1];
})
} else if (pricefilter) {
return cars.filter(function (car) {
return car.price >= pricefilter[0] && car.price <= pricefilter[1];
})
}
else return cars
The result in the example above should output {name:'Ferrari', speed:240, price: 100}
What would be the way to do it with javascript filter? Thanks in advance!
You could create a filterCar method via prototype inheritance
Array.prototype.filterCar = function(feature, range) {
return this.filter((el) => {
// is the range defined?
if (!!range.length) {
return el[feature] >= range[0] && el[feature] <= range[1];
}
else {
return true;
}
})
};
var cars = [
{name:'Ferrari', speed:240, price: 100},
{name:'Porsche', speed:220, price: 90},
{name:'Bugatti', speed:300, price: 500}
];
var result1 = cars.filterCar('speed', [240, 300])
.filterCar('price', [80, 120]));
var result2 = cars.filterCar('speed', [ ])
.filterCar('price', [80, 120]));
console.log(result1); // [{name: "Ferrari", speed: 240, price: 100}]
console.log(result2); /* [{name: "Ferrari", speed: 240, price: 100},
{name: "Porsche", speed: 220, price: 90}] */
You can wrap your filtering up into a re-usable method, and this can account for the filter not being available.
function filterCars(carsArray, property, rangeArray) {
// if rangeArray is not supplied, or is empty, just return the unfiltered input
if(!rangeArray|| rangeArray.length === 0) {
return carsArray;
}
// otherwise filter according to logic
return carsArray.filter(car => car[property] >= rangeArray[0] && car[property] <= rangeArray[1]);
}
This can be chained, or for more readability called in sequence:
function filterCars(carsArray, property, rangeArray) {
if(!rangeArray|| rangeArray.length === 0) {
return carsArray;
}
return carsArray.filter(car => car[property] >= rangeArray[0] && car[property] <= rangeArray[1]);
}
var speedfilter = []; // [240,300]
var pricefilter = [80,120]
var cars = [
{name:'Ferrari', maxspeed:240, price: 100},
{name:'Porsche', maxspeed:220, price: 90},
{name:'Bugatti', maxspeed:300, price: 500}
];
cars = filterCars(cars,"maxspeed",speedfilter);
cars = filterCars(cars,"price",pricefilter);
console.log(cars);

How do i filter array of objects nested in property of array objects?

I have model like this:
var model = [{id: 1, prices: [{count: 2}, {count: 3}]}, {id: 2, prices: [{count: 2}]}, {id: 3, prices: [{count: 3}]}];
and I need to filter this objects of array useing property count and I will need to return matched objects in three scenarios:
if the objects have two objects in array prices,
if the objects have one object in array prices matching count:2,
if the objects have one property in array prices matching count:3.
so..when i click the button without assigned value i wanna see all objects, when i click button with value = 2 i wanna see objects with count: 2 and when i click the button with value = 3 i wanna get objects with count: 3, i must do this in AngularJS –
maybe something like this?
var result = model.filter(function(m) {
// make sure the m.prices field exists and is an array
if (!m.prices || !Array.isArray(m.prices)) {
return false;
}
var numOfPrices = m.prices.length
if (numOfPrices === 2) { // return true if its length is 2
return true;
}
for (var i = 0; i < numOfPrices; i++) {
if (m.prices[i].count &&
(m.prices[i].count === 2 ||
m.prices[i].count == 3)) {
return true;
}
}
return false;
});
use lodash or underscore library.. and then your code with lodash will be like:
_.filter(model, function(i){
return _.intersection(_.map(i.prices, 'count'), [3,2]).length;
})
it returns items that on their price property have array which contains element with count = 3 or count = 2
var model = [{
id: 1,
prices: [{
count: 2
}, {
count: 3
}]
}, {
id: 2,
prices: [{
count: 2
}]
}, {
id: 3,
prices: [{
count: 3
}]
}];
var search = function(data) {
var result = {};
function arrayObjectIndexOf(myArray, searchTerm, property) {
for (var i = 0, len = myArray.length; i < len; i++) {
if (myArray[i][property] === searchTerm) return i;
}
return -1;
}
for (var index in data) {
if (data[index].hasOwnProperty("prices") && arrayObjectIndexOf(data[index].prices, 2, 'count') != -1) {
result[data[index].id] = data[index];
} else if (data[index].hasOwnProperty("prices") && arrayObjectIndexOf(data[index].prices, 3, 'count') != -1) {
result[data[index].id] = data[index];
} else if (data[index].hasOwnProperty("prices") &&
data[index].prices.length == 2) {
result[data[index].id] = data[index];
}
}
return result;
}
var output = search(model);
console.log(output);

Categories