Javascript 填充数组中缺少的数值
假设我有一个数组Javascript 填充数组中缺少的数值,javascript,ramda.js,Javascript,Ramda.js,假设我有一个数组[0,null,null,3,null,null,null,null,11] 我想用基于上一个和下一个已知数字(和索引?)的数字填充空值,因此得到[0,1,2,3,5,7,9,11]。最有效的方法是什么 我在想一种方法,可以计算两个已知数字之间的空值,然后得到一步的大小。但这些步骤在两人之间是不同的 我正在制作一个图表,其中可能缺少一些值,因此我必须填写可能的值 这是我尝试过的,但我认为这是非常低效和混乱的。我更喜欢使用ramda.js或一些函数方法 const data=[0
[0,null,null,3,null,null,null,null,11]
我想用基于上一个和下一个已知数字(和索引?)的数字填充空值,因此得到[0,1,2,3,5,7,9,11]
。最有效的方法是什么
我在想一种方法,可以计算两个已知数字之间的空值,然后得到一步的大小。但这些步骤在两人之间是不同的
我正在制作一个图表,其中可能缺少一些值,因此我必须填写可能的值
这是我尝试过的,但我认为这是非常低效和混乱的。我更喜欢使用ramda.js或一些函数方法
const data=[0,null,null,3,null,null,null,11]
const getStep=(arr,lastKnown=0,counter=1)=>{
常数val=arr[0];
如果(val!==null){
返回(val-最近已知)/计数器
}否则{
返回getStep(arr.slice(1),lastKnown,++计数器)
}
}
let lastKnown=null
让currentStep=null
常量filledData=data.map((x,i)=>{
如果(x!==null){
lastKnown=x
currentStep=null
返回x
}
如果(currentStep!==null){
lastKnown=lastKnown+currentStep
}否则{
currentStep=getStep(data.slice(i),最后已知)
}
返回currentStep+lastKnown
})
console.log(filledData)
执行此操作的一种方法是使用for循环和计数:
var skips = 0;
var last;
for (var i=0; i<arr.length; i++){
var current = arr[i]
if (current !== null) {
// If there are skipped spots that need to be filled...
if (skips > 0){
// Calculate interval based on on skip count, and difference between current and last
var interval = (current-arr[last])/(skips+1);
// Fill in the missing spots in original array
for (var j=1; j<=skips; j++){
arr[last+j] = arr[last]+(interval*j)
}
}
last = i; // update last valid index
skips = 0; // reset skip count
}
// If null, just increment skip count
else {
skips++
}
}
var跳过=0;
var last;
对于(变量i=0;i 0){
//基于跳过计数以及当前和上次之间的差值计算间隔
var间隔=(当前arr[上次]/(跳过+1);
//填充原始数组中缺少的点
对于(var j=1;j,您可以迭代数组,如果找到null
值,则会提前查看下一个数字和间隙,直到采用线性方法填充该数字
var数组=[0,null,null,3,null,null,null,11],
i=0,j,δ;
while(i console.log(数组);
以下是我使用ramda的快速解决方案:
const xs = [0, null, null, 3, null, null, null, 11]
const scanWithIndex = R.addIndex(R.scan)
const notNil = R.complement(R.isNil)
const mapWithIndex = R.addIndex(R.map)
const zipArrays = R.zipWith(R.concat)
// number of cons nulls for nth element
const consNulls = R.drop(1, R.scan((acc, x) => R.isNil(x) ? (acc + 1) : 0, 0, xs))
// length of ongoing null sequence for each element
const consNullsSeqLens = R.drop(1, scanWithIndex((acc, x, ind) =>{
if (x !== 0 && acc !== 0) return acc
const rest = R.drop(ind, consNulls)
return R.findIndex(R.equals(0), rest)
}, 0, consNulls))
// previous non null value for each el
const prevNonNulls = R.scan((acc, x) => R.isNil(x) ? acc : x, 0, xs)
// next non null value for each el
const nextNonNulls = mapWithIndex((x, ind) => {
const rest = R.drop(ind, xs)
return R.find(notNil, rest)
}, xs)
// function to calculate missing values based on zipped arrays
const calculateMissingValue = ([x, seqN, seqLen, next, prev]) =>
R.isNil(x) ? prev + (next - prev) / (seqLen + 1) * seqN : x
R.map(
calculateMissingValue,
// zips 5 lists together
zipArrays(
zipWith(R.append, consNullsSeqLens, R.zip(xs, consNulls)),
R.zip(nextNonNulls,prevNonNulls)
)
)
虽然从节目中可以看出,您可以使用Ramda来实现这一点,但我不确定Ramda是否会有很大帮助。(免责声明:我是Ramda的作者之一。)
我第一次看这个是这样的:
const intersperseNulls = pipe(
reduce(
({vals, prev, nilCount}, curr) => isNil(curr)
? {vals: vals, prev: prev, nilCount: nilCount + 1}
: (nilCount < 1)
? {vals: append(curr, vals), prev: curr, nilCount: 0}
: {
vals: append(curr, concat(vals, times(n => prev + (n + 1) * (curr - prev) / (nilCount + 1), nilCount))),
prev: curr,
nilCount: 0
},
{vals: [], prev: undefined, nilCount: 0},
),
prop('vals')
);
只有次
难以更换
但最后,我更喜欢from。它更简单,更容易阅读,而且不需要任何诡计
您可以在中看到这些。另一种方法是将输入数组转换为一个“段”列表,捕获每个段的起始值、结束值和大小。然后,您可以使用在每个段的起始值和结束值之间的线性步长来建立列表
const input=[0,null,null,3,null,null,null,11]
//递归地将稀疏的数字列表转换为段列表
const segmentNull=xs=>{
如果(xs.length==0){
返回[]
}否则{
常数[y,…ys]=xs
常数计数=R.takeWhile(R.isNil,ys)。长度+1
const next=R.dropWhile(R.isNil,ys)
返回next.length>0
?R.prepend({start:y,end:next[0],count},segmentNull(next))
: []
}
}
//段空(输入)
//=>[{“计数”:3,“结束”:3,“开始”:0},{“计数”:4,“结束”:11,“开始”:3}]
//在“开始”和“结束”值之间线性生成“计数”值列表
常数线性排列=(开始、结束、计数)=>
R.次(n=>(结束-开始)*(n+1)/计数+开始,计数)
//线性排列(3、11、4)
//=> [5, 7, 9, 11]
//将线段列表转换为线段之间的线性值列表
const buildListFromSegments=R.chain({start,end,count})=>
线性排列(开始、结束、计数))
//buildListFromSegments(segmentNull(输入))
//=> [1, 2, 3, 5, 7, 9, 11]
//^--请注意,缺少前导0
//在“buildListFromSegments”的结果前面加上初始值`
const fn=xs=>R.prepend(xs[0],buildListFromSegments(segmentNull(xs)))
console.log(fn(输入))
一种O(n*m)解,其中n是所有元素的计数,m是空值的计数
算法假设在索引位置0和长度1处始终存在有效的数字
函数填充空格(a){
var s,//步骤
r=a.reduce(函数([t,ns,r],e){/[temp,nulls数组,结果累加器]
e===null?ns.push(e)
:t==void 0?t=e
:(s=(e-t)/(ns.长度+1),
r、 推(t,…ns.map((u,i)=>t+(i+1)*s)),
ns=[],
t=e);
返回[t,ns,r];
},[void 0,[],[]]);
返回r[2]。concat(r[0]);
}
var arr=[0,null,null,3,null,null,null,11],
res=填充空白(arr);
console.log(JSON.stringify(res));
您可以添加代码(an)你已经尝试过了。我已经添加了我的尝试,但是我有点为这个代码感到羞耻:如果列表以null
开头或结尾,那么预期的行为是什么?你能让这个答案符合TS/ES6吗?这看起来很混乱。嗯,这似乎在我们的测试中失败了。谢谢。有些值没有正确推断请添加你的test数组。它太大,无法在此处添加,但我将提供1secacutally-no链接。显然,我们正在尝试匹配的另一个C#lib存在此问题。因此,您的代码工作得非常好。很抱歉发出此警报。我的错。
const steps = (b, e, c) => {
const results = []
for (let i = 0; i < c; i++) {results.push(b + (i + 1) * (e - b) / (c + 1));}
return results;
}
const intersperseNulls = array => array.reduce(
({vals, prev, nilCount}, curr) => (curr == null)
? {vals: vals, prev: prev, nilCount: nilCount + 1}
: (nilCount < 1)
? {vals: vals.concat(curr), prev: curr, nilCount: 0}
: {
vals: vals.concat(steps(prev, curr, nilCount)).concat(curr),
prev: curr,
nilCount: 0
},
{vals: [], prev: undefined, nilCount: 0},
).vals